[med-svn] [SCM] cufflinks branch, master, updated. upstream/0.9.3-16-gfedd24b

Alex Mestiashvili alex at biotec.tu-dresden.de
Tue May 24 19:02:34 UTC 2011


The following commit has been merged in the master branch:
commit fecac763336b5f7878e530995e365b031b3c6dd3
Author: Alex Mestiashvili <alex at biotec.tu-dresden.de>
Date:   Tue May 24 18:25:18 2011 +0200

    Imported upstream version 1.0.2

diff --git a/AUTHORS b/AUTHORS
index 2a91f4d..7b48638 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -11,3 +11,6 @@ the abundance estimation model used by Cufflinks and Cuffdiff.
  TopHat:			 http://tophat.cbcb.umd.edu
  Bowtie:             http://bowtie-bio.sf.net
 
+As of version 1.0, Cufflinks depends on and includes LOCFIT, a regression package originally written 
+by Catherine Loader and Jiayang Sun. Some modifications were made to LOCFIT.  Modified source
+for LOCFIT lives in src/locfit.
diff --git a/Makefile.in b/Makefile.in
index 506c75c..bc5d3c8 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -120,6 +120,11 @@ PACKAGE_STRING = @PACKAGE_STRING@
 PACKAGE_TARNAME = @PACKAGE_TARNAME@
 PACKAGE_VERSION = @PACKAGE_VERSION@
 PATH_SEPARATOR = @PATH_SEPARATOR@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
 RANLIB = @RANLIB@
 SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
@@ -161,8 +166,12 @@ localstatedir = @localstatedir@
 mandir = @mandir@
 mkdir_p = @mkdir_p@
 oldincludedir = @oldincludedir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 sysconfdir = @sysconfdir@
diff --git a/aclocal.m4 b/aclocal.m4
index 4ea3a5b..7f99a5a 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -672,6 +672,170 @@ AC_DEFUN([_AM_SET_OPTIONS],
 AC_DEFUN([_AM_IF_OPTION],
 [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
 
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+# ---------------------------------------------------------------------------
+# Adds support for distributing Python modules and packages.  To
+# install modules, copy them to $(pythondir), using the python_PYTHON
+# automake variable.  To install a package with the same name as the
+# automake package, install to $(pkgpythondir), or use the
+# pkgpython_PYTHON automake variable.
+#
+# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as
+# locations to install python extension modules (shared libraries).
+# Another macro is required to find the appropriate flags to compile
+# extension modules.
+#
+# If your package is configured with a different prefix to python,
+# users will have to add the install directory to the PYTHONPATH
+# environment variable, or create a .pth file (see the python
+# documentation for details).
+#
+# If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will
+# cause an error if the version of python installed on the system
+# doesn't meet the requirement.  MINIMUM-VERSION should consist of
+# numbers and dots only.
+AC_DEFUN([AM_PATH_PYTHON],
+ [
+  dnl Find a Python interpreter.  Python versions prior to 1.5 are not
+  dnl supported because the default installation locations changed from
+  dnl $prefix/lib/site-python in 1.4 to $prefix/lib/python1.5/site-packages
+  dnl in 1.5.
+  m4_define_default([_AM_PYTHON_INTERPRETER_LIST],
+                    [python python2 python2.5 python2.4 python2.3 python2.2 dnl
+python2.1 python2.0 python1.6 python1.5])
+
+  m4_if([$1],[],[
+    dnl No version check is needed.
+    # Find any Python interpreter.
+    if test -z "$PYTHON"; then
+      AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :)
+    fi
+    am_display_PYTHON=python
+  ], [
+    dnl A version check is needed.
+    if test -n "$PYTHON"; then
+      # If the user set $PYTHON, use it and don't search something else.
+      AC_MSG_CHECKING([whether $PYTHON version >= $1])
+      AM_PYTHON_CHECK_VERSION([$PYTHON], [$1],
+			      [AC_MSG_RESULT(yes)],
+			      [AC_MSG_ERROR(too old)])
+      am_display_PYTHON=$PYTHON
+    else
+      # Otherwise, try each interpreter until we find one that satisfies
+      # VERSION.
+      AC_CACHE_CHECK([for a Python interpreter with version >= $1],
+	[am_cv_pathless_PYTHON],[
+	for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do
+	  test "$am_cv_pathless_PYTHON" = none && break
+	  AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break])
+	done])
+      # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON.
+      if test "$am_cv_pathless_PYTHON" = none; then
+	PYTHON=:
+      else
+        AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON])
+      fi
+      am_display_PYTHON=$am_cv_pathless_PYTHON
+    fi
+  ])
+
+  if test "$PYTHON" = :; then
+  dnl Run any user-specified action, or abort.
+    m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])])
+  else
+
+  dnl Query Python for its version number.  Getting [:3] seems to be
+  dnl the best way to do this; it's what "site.py" does in the standard
+  dnl library.
+
+  AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version],
+    [am_cv_python_version=`$PYTHON -c "import sys; print sys.version[[:3]]"`])
+  AC_SUBST([PYTHON_VERSION], [$am_cv_python_version])
+
+  dnl Use the values of $prefix and $exec_prefix for the corresponding
+  dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX.  These are made
+  dnl distinct variables so they can be overridden if need be.  However,
+  dnl general consensus is that you shouldn't need this ability.
+
+  AC_SUBST([PYTHON_PREFIX], ['${prefix}'])
+  AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}'])
+
+  dnl At times (like when building shared libraries) you may want
+  dnl to know which OS platform Python thinks this is.
+
+  AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform],
+    [am_cv_python_platform=`$PYTHON -c "import sys; print sys.platform"`])
+  AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform])
+
+
+  dnl Set up 4 directories:
+
+  dnl pythondir -- where to install python scripts.  This is the
+  dnl   site-packages directory, not the python standard library
+  dnl   directory like in previous automake betas.  This behavior
+  dnl   is more consistent with lispdir.m4 for example.
+  dnl Query distutils for this directory.  distutils does not exist in
+  dnl Python 1.5, so we fall back to the hardcoded directory if it
+  dnl doesn't work.
+  AC_CACHE_CHECK([for $am_display_PYTHON script directory],
+    [am_cv_python_pythondir],
+    [am_cv_python_pythondir=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_lib(0,0,prefix='$PYTHON_PREFIX')" 2>/dev/null ||
+     echo "$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages"`])
+  AC_SUBST([pythondir], [$am_cv_python_pythondir])
+
+  dnl pkgpythondir -- $PACKAGE directory under pythondir.  Was
+  dnl   PYTHON_SITE_PACKAGE in previous betas, but this naming is
+  dnl   more consistent with the rest of automake.
+
+  AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE])
+
+  dnl pyexecdir -- directory for installing python extension modules
+  dnl   (shared libraries)
+  dnl Query distutils for this directory.  distutils does not exist in
+  dnl Python 1.5, so we fall back to the hardcoded directory if it
+  dnl doesn't work.
+  AC_CACHE_CHECK([for $am_display_PYTHON extension module directory],
+    [am_cv_python_pyexecdir],
+    [am_cv_python_pyexecdir=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_lib(1,0,prefix='$PYTHON_EXEC_PREFIX')" 2>/dev/null ||
+     echo "${PYTHON_EXEC_PREFIX}/lib/python${PYTHON_VERSION}/site-packages"`])
+  AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir])
+
+  dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE)
+
+  AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE])
+
+  dnl Run any user-specified action.
+  $2
+  fi
+
+])
+
+
+# AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
+# ---------------------------------------------------------------------------
+# Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION.
+# Run ACTION-IF-FALSE otherwise.
+# This test uses sys.hexversion instead of the string equivalent (first
+# word of sys.version), in order to cope with versions such as 2.2c1.
+# hexversion has been introduced in Python 1.5.2; it's probably not
+# worth to support older versions (1.5.1 was released on October 31, 1998).
+AC_DEFUN([AM_PYTHON_CHECK_VERSION],
+ [prog="import sys, string
+# split strings by '.' and convert to numeric.  Append some zeros
+# because we need at least 4 digits for the hex conversion.
+minver = map(int, string.split('$2', '.')) + [[0, 0, 0]]
+minverhex = 0
+for i in xrange(0, 4): minverhex = (minverhex << 8) + minver[[i]]
+sys.exit(sys.hexversion < minverhex)"
+  AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])])
+
 # Copyright (C) 2001, 2003, 2005  Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
diff --git a/configure b/configure
index 11025c1..f3bbd5c 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.59 for cufflinks 0.9.3.
+# Generated by GNU Autoconf 2.59 for cufflinks 1.0.2.
 #
 # Report bugs to <cole at cs.umd.edu>.
 #
@@ -269,8 +269,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
 # Identity of this package.
 PACKAGE_NAME='cufflinks'
 PACKAGE_TARNAME='cufflinks'
-PACKAGE_VERSION='0.9.3'
-PACKAGE_STRING='cufflinks 0.9.3'
+PACKAGE_VERSION='1.0.2'
+PACKAGE_STRING='cufflinks 1.0.2'
 PACKAGE_BUGREPORT='cole at cs.umd.edu'
 
 ac_unique_file="config.h.in"
@@ -311,7 +311,7 @@ ac_includes_default="\
 # include <unistd.h>
 #endif"
 
-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar CXX CXXFLAGS LDFLAGS CPPFLAGS ac_ct_CXX EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE CC CFLAGS ac_ct_CC CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE RANLIB ac_ct_RANLIB BOOST_CPPFLAGS BOOST_LDFLAGS BAM_CPPFLAGS BAM_LDFLAGS BAM_LIB build build_cpu build_vendor build_os BOOST_THREAD_LIB CPP EGREP ZLIB host host_cpu host_vendor host_os LIBOBJS LTLIBOBJS'
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar PYTHON CXX CXXFLAGS LDFLAGS CPPFLAGS ac_ct_CXX EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE CC CFLAGS ac_ct_CC CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE RANLIB ac_ct_RANLIB PYTHON_VERSION PYTHON_PREFIX PYTHON_EXEC_PREFIX PYTHON_PLATFORM pythondir pkgpythondir pyexecdir pkgpyexecdir BOOST_CPPFLAGS BOOST_LDFLAGS BAM_CPPFLAGS BAM_LDFLAGS BAM_LIB build build_cpu build_vendor build_os BOOST_THREAD_LIB CPP EGREP ZLIB host host_cpu host_vendor host_os LIBOBJS LTLIBOBJS'
 ac_subst_files=''
 
 # Initialize some variables set by options.
@@ -752,6 +752,10 @@ ac_env_target_alias_set=${target_alias+set}
 ac_env_target_alias_value=$target_alias
 ac_cv_env_target_alias_set=${target_alias+set}
 ac_cv_env_target_alias_value=$target_alias
+ac_env_PYTHON_set=${PYTHON+set}
+ac_env_PYTHON_value=$PYTHON
+ac_cv_env_PYTHON_set=${PYTHON+set}
+ac_cv_env_PYTHON_value=$PYTHON
 ac_env_CXX_set=${CXX+set}
 ac_env_CXX_value=$CXX
 ac_cv_env_CXX_set=${CXX+set}
@@ -788,7 +792,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures cufflinks 0.9.3 to adapt to many kinds of systems.
+\`configure' configures cufflinks 1.0.2 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -854,7 +858,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of cufflinks 0.9.3:";;
+     short | recursive ) echo "Configuration of cufflinks 1.0.2:";;
    esac
   cat <<\_ACEOF
 
@@ -899,6 +903,7 @@ Optional Packages:
   --without-zlib to disable zlib usage completely
 
 Some influential environment variables:
+  PYTHON      python program
   CXX         C++ compiler command
   CXXFLAGS    C++ compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
@@ -1008,7 +1013,7 @@ fi
 test -n "$ac_init_help" && exit 0
 if $ac_init_version; then
   cat <<\_ACEOF
-cufflinks configure 0.9.3
+cufflinks configure 1.0.2
 generated by GNU Autoconf 2.59
 
 Copyright (C) 2003 Free Software Foundation, Inc.
@@ -1022,7 +1027,7 @@ cat >&5 <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by cufflinks $as_me 0.9.3, which was
+It was created by cufflinks $as_me 1.0.2, which was
 generated by GNU Autoconf 2.59.  Invocation command line was
 
   $ $0 $@
@@ -1360,7 +1365,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 
 cat >>confdefs.h <<\_ACEOF
-#define SVN_REVISION "1649"
+#define SVN_REVISION "2335"
 _ACEOF
 
 
@@ -1674,7 +1679,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='cufflinks'
- VERSION='0.9.3'
+ VERSION='1.0.2'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -1807,6 +1812,8 @@ am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
 
 #AM_PATH_CPPUNIT(1.10.2)
 
+
+
  # Make sure CXXFLAGS is defined so that AC_PROG_CXX doesn't set it.
 CXXFLAGS="$CXXFLAGS"
 CFLAGS="$CFLAGS"
@@ -3618,6 +3625,191 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
 
 
 
+
+        if test -n "$PYTHON"; then
+      # If the user set $PYTHON, use it and don't search something else.
+      echo "$as_me:$LINENO: checking whether $PYTHON version >= 2.4" >&5
+echo $ECHO_N "checking whether $PYTHON version >= 2.4... $ECHO_C" >&6
+      prog="import sys, string
+# split strings by '.' and convert to numeric.  Append some zeros
+# because we need at least 4 digits for the hex conversion.
+minver = map(int, string.split('2.4', '.')) + [0, 0, 0]
+minverhex = 0
+for i in xrange(0, 4): minverhex = (minverhex << 8) + minver[i]
+sys.exit(sys.hexversion < minverhex)"
+  if { echo "$as_me:$LINENO: $PYTHON -c "$prog"" >&5
+   ($PYTHON -c "$prog") >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); }; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+else
+  { { echo "$as_me:$LINENO: error: too old" >&5
+echo "$as_me: error: too old" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+      am_display_PYTHON=$PYTHON
+    else
+      # Otherwise, try each interpreter until we find one that satisfies
+      # VERSION.
+      echo "$as_me:$LINENO: checking for a Python interpreter with version >= 2.4" >&5
+echo $ECHO_N "checking for a Python interpreter with version >= 2.4... $ECHO_C" >&6
+if test "${am_cv_pathless_PYTHON+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+
+	for am_cv_pathless_PYTHON in python python2 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 python1.6 python1.5 none; do
+	  test "$am_cv_pathless_PYTHON" = none && break
+	  prog="import sys, string
+# split strings by '.' and convert to numeric.  Append some zeros
+# because we need at least 4 digits for the hex conversion.
+minver = map(int, string.split('2.4', '.')) + [0, 0, 0]
+minverhex = 0
+for i in xrange(0, 4): minverhex = (minverhex << 8) + minver[i]
+sys.exit(sys.hexversion < minverhex)"
+  if { echo "$as_me:$LINENO: $am_cv_pathless_PYTHON -c "$prog"" >&5
+   ($am_cv_pathless_PYTHON -c "$prog") >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); }; then
+  break
+fi
+
+	done
+fi
+echo "$as_me:$LINENO: result: $am_cv_pathless_PYTHON" >&5
+echo "${ECHO_T}$am_cv_pathless_PYTHON" >&6
+      # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON.
+      if test "$am_cv_pathless_PYTHON" = none; then
+	PYTHON=:
+      else
+        # Extract the first word of "$am_cv_pathless_PYTHON", so it can be a program name with args.
+set dummy $am_cv_pathless_PYTHON; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_path_PYTHON+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  case $PYTHON in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  ;;
+esac
+fi
+PYTHON=$ac_cv_path_PYTHON
+
+if test -n "$PYTHON"; then
+  echo "$as_me:$LINENO: result: $PYTHON" >&5
+echo "${ECHO_T}$PYTHON" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+      fi
+      am_display_PYTHON=$am_cv_pathless_PYTHON
+    fi
+
+
+  if test "$PYTHON" = :; then
+      { { echo "$as_me:$LINENO: error: no suitable Python interpreter found" >&5
+echo "$as_me: error: no suitable Python interpreter found" >&2;}
+   { (exit 1); exit 1; }; }
+  else
+
+
+  echo "$as_me:$LINENO: checking for $am_display_PYTHON version" >&5
+echo $ECHO_N "checking for $am_display_PYTHON version... $ECHO_C" >&6
+if test "${am_cv_python_version+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  am_cv_python_version=`$PYTHON -c "import sys; print sys.version[:3]"`
+fi
+echo "$as_me:$LINENO: result: $am_cv_python_version" >&5
+echo "${ECHO_T}$am_cv_python_version" >&6
+  PYTHON_VERSION=$am_cv_python_version
+
+
+
+  PYTHON_PREFIX='${prefix}'
+
+  PYTHON_EXEC_PREFIX='${exec_prefix}'
+
+
+
+  echo "$as_me:$LINENO: checking for $am_display_PYTHON platform" >&5
+echo $ECHO_N "checking for $am_display_PYTHON platform... $ECHO_C" >&6
+if test "${am_cv_python_platform+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  am_cv_python_platform=`$PYTHON -c "import sys; print sys.platform"`
+fi
+echo "$as_me:$LINENO: result: $am_cv_python_platform" >&5
+echo "${ECHO_T}$am_cv_python_platform" >&6
+  PYTHON_PLATFORM=$am_cv_python_platform
+
+
+
+
+                echo "$as_me:$LINENO: checking for $am_display_PYTHON script directory" >&5
+echo $ECHO_N "checking for $am_display_PYTHON script directory... $ECHO_C" >&6
+if test "${am_cv_python_pythondir+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  am_cv_python_pythondir=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_lib(0,0,prefix='$PYTHON_PREFIX')" 2>/dev/null ||
+     echo "$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages"`
+fi
+echo "$as_me:$LINENO: result: $am_cv_python_pythondir" >&5
+echo "${ECHO_T}$am_cv_python_pythondir" >&6
+  pythondir=$am_cv_python_pythondir
+
+
+
+  pkgpythondir=\${pythondir}/$PACKAGE
+
+
+            echo "$as_me:$LINENO: checking for $am_display_PYTHON extension module directory" >&5
+echo $ECHO_N "checking for $am_display_PYTHON extension module directory... $ECHO_C" >&6
+if test "${am_cv_python_pyexecdir+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  am_cv_python_pyexecdir=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_lib(1,0,prefix='$PYTHON_EXEC_PREFIX')" 2>/dev/null ||
+     echo "${PYTHON_EXEC_PREFIX}/lib/python${PYTHON_VERSION}/site-packages"`
+fi
+echo "$as_me:$LINENO: result: $am_cv_python_pyexecdir" >&5
+echo "${ECHO_T}$am_cv_python_pyexecdir" >&6
+  pyexecdir=$am_cv_python_pyexecdir
+
+
+
+  pkgpyexecdir=\${pyexecdir}/$PACKAGE
+
+
+
+  fi
+
+
+
+
+
 # Check whether --with-boost or --without-boost was given.
 if test "${with_boost+set}" = set; then
   withval="$with_boost"
@@ -4014,7 +4206,7 @@ cat confdefs.h >>conftest.$ac_ext
 cat >>conftest.$ac_ext <<_ACEOF
 /* end confdefs.h.  */
 
-	#include <bam/bam.h>
+	#include <samtools/bam.h>
 
 int
 main ()
@@ -6545,7 +6737,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='cufflinks'
- VERSION='0.9.3'
+ VERSION='1.0.2'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -7375,7 +7567,7 @@ _ASBOX
 } >&5
 cat >&5 <<_CSEOF
 
-This file was extended by cufflinks $as_me 0.9.3, which was
+This file was extended by cufflinks $as_me 1.0.2, which was
 generated by GNU Autoconf 2.59.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -7438,7 +7630,7 @@ _ACEOF
 
 cat >>$CONFIG_STATUS <<_ACEOF
 ac_cs_version="\\
-cufflinks config.status 0.9.3
+cufflinks config.status 1.0.2
 configured by $0, generated by GNU Autoconf 2.59,
   with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
 
@@ -7659,6 +7851,7 @@ s, at am__leading_dot@,$am__leading_dot,;t t
 s, at AMTAR@,$AMTAR,;t t
 s, at am__tar@,$am__tar,;t t
 s, at am__untar@,$am__untar,;t t
+s, at PYTHON@,$PYTHON,;t t
 s, at CXX@,$CXX,;t t
 s, at CXXFLAGS@,$CXXFLAGS,;t t
 s, at LDFLAGS@,$LDFLAGS,;t t
@@ -7683,6 +7876,14 @@ s, at am__fastdepCC_TRUE@,$am__fastdepCC_TRUE,;t t
 s, at am__fastdepCC_FALSE@,$am__fastdepCC_FALSE,;t t
 s, at RANLIB@,$RANLIB,;t t
 s, at ac_ct_RANLIB@,$ac_ct_RANLIB,;t t
+s, at PYTHON_VERSION@,$PYTHON_VERSION,;t t
+s, at PYTHON_PREFIX@,$PYTHON_PREFIX,;t t
+s, at PYTHON_EXEC_PREFIX@,$PYTHON_EXEC_PREFIX,;t t
+s, at PYTHON_PLATFORM@,$PYTHON_PLATFORM,;t t
+s, at pythondir@,$pythondir,;t t
+s, at pkgpythondir@,$pkgpythondir,;t t
+s, at pyexecdir@,$pyexecdir,;t t
+s, at pkgpyexecdir@,$pkgpyexecdir,;t t
 s, at BOOST_CPPFLAGS@,$BOOST_CPPFLAGS,;t t
 s, at BOOST_LDFLAGS@,$BOOST_LDFLAGS,;t t
 s, at BAM_CPPFLAGS@,$BAM_CPPFLAGS,;t t
@@ -8439,3 +8640,8 @@ echo \
   Email <${PACKAGE_BUGREPORT}> with questions and bug reports.
 "
 
+if test x"${PYTHON}" = x":"  ||  ! test -x "${PYTHON}"; then
+    echo "WARNING! python was not found and is required to run some utility scripts"
+    echo "  We recommend installing python and pointing configure to the installed location"
+fi
+
diff --git a/configure.ac b/configure.ac
index f381189..1fb6577 100755
--- a/configure.ac
+++ b/configure.ac
@@ -4,7 +4,7 @@ m4_include([ax_bam.m4])
 m4_include([ax_check_zlib.m4])
 
 define([svnversion], esyscmd([sh -c "svnversion|tr -d '\n'"]))dnl
-AC_INIT([cufflinks], [0.9.3], [cole at cs.umd.edu])
+AC_INIT([cufflinks], [1.0.2], [cole at cs.umd.edu])
 AC_DEFINE(SVN_REVISION, "svnversion", [SVN Revision])
 
 AC_CONFIG_SRCDIR([config.h.in])
@@ -14,6 +14,8 @@ AM_INIT_AUTOMAKE
 
 #AM_PATH_CPPUNIT(1.10.2)
 
+AC_ARG_VAR(PYTHON, [python program])
+
  # Make sure CXXFLAGS is defined so that AC_PROG_CXX doesn't set it.
 CXXFLAGS="$CXXFLAGS"
 CFLAGS="$CFLAGS"
@@ -27,6 +29,7 @@ AC_PROG_CC
 AC_PROG_MAKE_SET
 AC_PROG_RANLIB
 AC_PROG_INSTALL
+AM_PATH_PYTHON([2.4])
 AX_BOOST_BASE([1.38.0])
 AX_BAM
 AX_BOOST_THREAD
@@ -132,3 +135,8 @@ echo \
   Email <${PACKAGE_BUGREPORT}> with questions and bug reports.
 "
 
+if test x"${PYTHON}" = x":"  ||  ! test -x "${PYTHON}"; then
+    echo "WARNING! python was not found and is required to run some utility scripts"
+    echo "  We recommend installing python and pointing configure to the installed location"
+fi
+
diff --git a/debian/changelog b/debian/changelog
index 68447dd..5b3ecc2 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+cufflinks (1.0.2-1) unstable; urgency=low
+
+  * Upstream release 
+
+ -- Alex Mestiashvili <alex at biotec.tu-dresden.de>  Tue, 24 May 2011 13:43:09 +0200
+
 cufflinks (0.9.3-1) unstable; urgency=low
 
   * Initial release (Closes: #nnnn)  <nnnn is the bug number of your ITP>
diff --git a/debian/compress_gtf.1 b/debian/compress_gtf.1
new file mode 100644
index 0000000..c7a7877
--- /dev/null
+++ b/debian/compress_gtf.1
@@ -0,0 +1,19 @@
+.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.39.4.
+.TH COMPRESS_GTF: "1" "May 2011" "compress_gtf" "User Commands"
+.SH NAME
+compress_gtf: \- compress_gtf
+.SH SYNOPSIS
+.B compress_gtf
+[\fIoptions\fR] \fI<reference.gtf> <compressed_reference.gtf>\fR
+.SH OPTIONS
+\fB\-r\fR/\-\-reference\-seq                        reference fasta file                     [ default:   NULL ]
+
+\fB\-F\fR/\-\-raw\-fpkm                         use FPKM instead of isoform fraction
+
+\fB\-U\fR/\-\-union                   report projective union                  [ default:   OFF  ]
+
+\fB\-I\fR/\-\-intersection            report projective intersection           [ default:   ON   ]
+
+.PP
+.SH SEE ALSO
+http://cufflinks.cbcb.umd.edu/manual.html
diff --git a/debian/control b/debian/control
index 3b7554a..6687089 100644
--- a/debian/control
+++ b/debian/control
@@ -5,15 +5,15 @@ Maintainer: Debian Med Packaging Team <debian-med-packaging at lists.alioth.debian.
 DM-Upload-Allowed: yes
 Uploaders: Alex Mestiashvili <alex at biotec.tu-dresden.de>
 Build-Depends: debhelper (>= 7.0.50~), autotools-dev , libboost-dev (>=1.38.0) , libbam-dev
-Standards-Version: 3.9.1
+Standards-Version: 3.9.2
 Homepage: http://cufflinks.cbcb.umd.edu/
 Vcs-Git: git://git.debian.org/debian-med/cufflinks.git
 Vcs-Browser: http://git.debian.org/?p=debian-med/cufflinks.git;a=summary
 
 Package: cufflinks
 Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}
-Description: Transcript assembly, differential expression, and differential regulation for RNA-Seq
+Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}
+Description: Transcript assembly, differential expression and regulation for RNA-Seq
  Cufflinks assembles transcripts, estimates their abundances, and tests for
  differential expression and regulation in RNA-Seq samples. It accepts aligned
  RNA-Seq reads and assembles the alignments into a parsimonious set of
diff --git a/debian/copyright b/debian/copyright
index a81072b..c5c508b 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,15 +1,14 @@
-Format		: http://dep.debian.net/deps/dep5
-Upstream-Name	: cufflinks
-Source		: http://cufflinks.cbcb.umd.edu/downloads/
+Format      : http://dep.debian.net/deps/dep5
+Upstream-Name   : cufflinks
+Source      : http://cufflinks.cbcb.umd.edu/downloads/
 
 Files: *
-Copyright: <years> <put author's name and email here>
-           <years> <likewise for another author>
+Copyright: Copyright (C) 2003-2009 Cole Trapnell et al
 License: BSL-1
 
 Files: debian/*
 Copyright: 2011 Alex Mestiashvili <alex at biotec.tu-dresden.de>
-License: GPL-3.0+
+License: BSL-1
 
 License    :  BSL-1
 
diff --git a/debian/cuffcompare.1 b/debian/cuffcompare.1
new file mode 100644
index 0000000..5138968
--- /dev/null
+++ b/debian/cuffcompare.1
@@ -0,0 +1,53 @@
+.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.39.4.
+.TH CUFFCOMPARE "1" "May 2011" "cuffcompare v1.0.2 (2335)" "User Commands"
+.SH NAME
+cuffcompare \- helps analyze the transfrags
+.SH SYNOPSIS
+cuffcompare [\-r <reference_mrna.gtf>] [\-R] [\-T] [\-V] [\-s <seq_path>]
+.IP
+[\-o <outprefix>] [\-p <cprefix>]
+{\-i <input_gtf_list> | <input1.gtf> [<input2.gtf> .. <inputN.gtf>]}
+
+.IP
+.SH DESCRIPTION
+Cuffcompare provides classification, reference annotation mapping and various
+statistics for Cufflinks transfrags.
+Cuffcompare clusters and tracks transfrags across multiple samples, writing
+matching transcripts (intron chains) into <outprefix>.tracking, and a GTF
+file <outprefix>.combined.gtf containing a nonredundant set of transcripts
+across all input files (with a single representative transfrag chosen
+for each clique of matching transfrags across samples).
+.SH OPTIONS
+\fB\-i\fR provide a text file with a list of Cufflinks GTF files to process instead
+.IP
+of expecting them as command line arguments (useful when a large number
+of GTF files should be processed)
+.PP
+\fB\-r\fR  a set of known mRNAs to use as a reference for assessing
+.IP
+the accuracy of mRNAs or gene models given in <input.gtf>
+.PP
+\fB\-R\fR  for \fB\-r\fR option, reduce the set of reference transcripts to
+.IP
+only those found to overlap any of the input loci
+.PP
+\fB\-M\fR  discard (ignore) single\-exon transfrags and reference transcripts
+\fB\-N\fR  discard (ignore) single\-exon reference transcripts
+.PP
+\fB\-s\fR  <seq_path> can be a multi\-fasta file with all the genomic sequences or
+.IP
+a directory containing multiple single\-fasta files (one file per contig);
+lower case bases will be used to classify input transcripts as repeats
+.PP
+\fB\-d\fR  max distance (range) for grouping transcript start sites (100)
+\fB\-p\fR  the name prefix to use for consensus transcripts in the
+.IP
+<outprefix>.combined.gtf file (default: 'TCONS')
+.PP
+\fB\-C\fR  include the "contained" transcripts in the .combined.gtf file
+\fB\-G\fR  generic GFF input file(s) (do not assume Cufflinks GTF)
+\fB\-T\fR  do not generate .tmap and .refmap files for each input file
+\fB\-V\fR  verbose processing mode (showing all GFF parsing warnings)
+.PP
+.SH SEE ALSO
+http://cufflinks.cbcb.umd.edu/manual.html#cuffcompare
diff --git a/debian/cuffdiff.1 b/debian/cuffdiff.1
new file mode 100644
index 0000000..734eae6
--- /dev/null
+++ b/debian/cuffdiff.1
@@ -0,0 +1,90 @@
+.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.39.4.
+.TH CUFFDIFF: "1" "May 2011" "cuffdiff" "User Commands"
+.SH NAME
+cuffdiff \- find significant changes in transcript expression, splicing, and promoter use
+.SH SYNOPSIS
+.B cuffdiff
+[\fIoptions\fR] \fI<transcripts.gtf> <sample1_hits.sam> <sample2_hits.sam> \fR[... \fIsampleN_hits.sam\fR]
+
+Supply replicate SAMs as comma separated lists for each condition: sample1_rep1.sam,sample1_rep2.sam,...sample1_repM.sam
+.SH DESCRIPTION
+see online page http://cufflinks.cbcb.umd.edu/manual.html#cuffdiff
+.SH OPTIONS
+.TP
+\fB\-o\fR/\-\-output\-dir
+write all output files to this directory              [ default:     ./ ]
+.TP
+\fB\-T\fR/\-\-time\-series
+treat samples as a time\-series                        [ default:  FALSE ]
+.TP
+\fB\-c\fR/\-\-min\-alignment\-count
+minimum number of alignments in a locus for testing   [ default:   10 ]
+.TP
+\fB\-\-FDR\fR
+False discovery rate used in testing                  [ default:   0.05 ]
+.TP
+\fB\-M\fR/\-\-mask\-file
+ignore all alignment within transcripts in this file  [ default:   NULL ]
+.TP
+\fB\-b\fR/\-\-frag\-bias\-correct
+use bias correction \- reference fasta required        [ default:   NULL ]
+.TP
+\fB\-u\fR/\-\-multi\-read\-correct
+use 'rescue method' for multi\-reads (more accurate)   [ default:  FALSE ]
+.TP
+\fB\-N\fR/\-\-upper\-quartile\-norm
+use upper\-quartile normalization                      [ default:  FALSE ]
+.TP
+\fB\-L\fR/\-\-labels
+comma\-separated list of condition labels
+.TP
+\fB\-p\fR/\-\-num\-threads
+number of threads used during quantification          [ default:      1 ]
+.SS "Advanced Options:"
+.TP
+\fB\-\-library\-type\fR
+Library prep used for input reads                     [ default:  below ]
+.TP
+\fB\-m\fR/\-\-frag\-len\-mean
+average fragment length (unpaired reads only)         [ default:    200 ]
+.TP
+\fB\-s\fR/\-\-frag\-len\-std\-dev
+fragment length std deviation (unpaired reads only)   [ default:     80 ]
+.TP
+\fB\-\-num\-importance\-samples\fR
+number of importance samples for MAP restimation      [ default:   1000 ]
+.TP
+\fB\-\-max\-mle\-iterations\fR
+maximum iterations allowed for MLE calculation        [ default:   5000 ]
+.TP
+\fB\-\-compatible\-hits\-norm\fR
+count hits compatible with reference RNAs only        [ default:  TRUE  ]
+.TP
+\fB\-\-total\-hits\-norm\fR
+count all hits for normalization                      [ default:  FALSE ]
+.TP
+\fB\-\-poisson\-dispersion\fR
+Don't fit fragment counts for overdispersion          [ default:  FALSE ]
+.TP
+\fB\-v\fR/\-\-verbose
+log\-friendly verbose processing (no progress bar)     [ default:  FALSE ]
+.TP
+\fB\-q\fR/\-\-quiet
+log\-friendly quiet processing (no progress bar)       [ default:  FALSE ]
+.TP
+\fB\-\-no\-update\-check\fR
+do not contact server to check for update availability[ default:  FALSE ]
+.TP
+\fB\-\-emit\-count\-tables\fR
+print count tables used to fit overdispersion         [ default:  FALSE ]
+.SS "Supported library types:"
+.IP
+ff\-firststrand
+ff\-secondstrand
+ff\-unstranded
+fr\-firststrand
+fr\-secondstrand
+fr\-unstranded (default)
+transfrags
+.PP
+
diff --git a/debian/cufflinks.1 b/debian/cufflinks.1
new file mode 100644
index 0000000..3dfe393
--- /dev/null
+++ b/debian/cufflinks.1
@@ -0,0 +1,240 @@
+.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.39.4.
+.TH CUFFLINKS: "1" "May 2011" "cufflinks v1.0.2 '" "User Commands"
+.SH NAME
+cufflinks \- Transcript assembly, differential expression, and differential regulation for RNA-Seq
+.SH DESCRIPTION 
+see online page http://cufflinks.cbcb.umd.edu/manual.html
+.SH SYNOPSIS
+.B cufflinks
+[\fIoptions\fR] \fI<hits.sam>\fR
+.SH OPTIONS
+.TP
+\fB\-o\fR/\-\-output\-dir
+write all output files to this directory              [ default:     ./ ]
+.TP
+\fB\-p\fR/\-\-num\-threads
+number of threads used during analysis                [ default:      1 ]
+.TP
+\fB\-G\fR/\-\-GTF
+quantitate against reference transcript annotations
+.TP
+\fB\-g\fR/\-\-GTF\-guide
+use reference transcript annotation to guide assembly
+.TP
+\fB\-M\fR/\-\-mask\-file
+ignore all alignment within transcripts in this file
+.TP
+\fB\-b\fR/\-\-frag\-bias\-correct
+use bias correction \- reference fasta required        [ default:   NULL ]
+.TP
+\fB\-u\fR/\-\-multi\-read\-correct
+use 'rescue method' for multi\-reads (more accurate)   [ default:  FALSE ]
+.TP
+\fB\-\-library\-type\fR
+library prep used for input reads                     [ default:  below ]
+.SS "Advanced Abundance Estimation Options:"
+.TP
+\fB\-m\fR/\-\-frag\-len\-mean
+average fragment length (unpaired reads only)         [ default:    200 ]
+.TP
+\fB\-s\fR/\-\-frag\-len\-std\-dev
+fragment length std deviation (unpaired reads only)   [ default:     80 ]
+.TP
+\fB\-\-upper\-quartile\-norm\fR
+use upper\-quartile normalization                      [ default:  FALSE ]
+.TP
+\fB\-\-max\-mle\-iterations\fR
+maximum iterations allowed for MLE calculation        [ default:   5000 ]
+.TP
+\fB\-\-num\-importance\-samples\fR
+number of importance samples for MAP restimation      [ default:   1000 ]
+.TP
+\fB\-\-compatible\-hits\-norm\fR
+count hits compatible with reference RNAs only        [ default:  FALSE ]
+.TP
+\fB\-\-total\-hits\-norm\fR
+count all hits for normalization                      [ default:  TRUE  ]
+.SS "Advanced Assembly Options:"
+.TP
+\fB\-L\fR/\-\-label
+assembled transcripts have this ID prefix             [ default:   CUFF ]
+.TP
+\fB\-F\fR/\-\-min\-isoform\-fraction
+suppress transcripts below this abundance level       [ default:   0.10 ]
+.TP
+\fB\-j\fR/\-\-pre\-mrna\-fraction
+suppress intra\-intronic transcripts below this level  [ default:   0.15 ]
+.TP
+\fB\-I\fR/\-\-max\-intron\-length
+ignore alignments with gaps longer than this          [ default: 300000 ]
+.TP
+\fB\-a\fR/\-\-junc\-alpha
+alpha for junction binomial test filter               [ default:  0.001 ]
+.TP
+\fB\-A\fR/\-\-small\-anchor\-fraction
+percent read overhang taken as 'suspiciously small'   [ default:   0.09 ]
+.TP
+\fB\-\-min\-frags\-per\-transfrag\fR
+minimum number of fragments needed for new transfrags [ default:     10 ]
+.TP
+\fB\-\-overhang\-tolerance\fR
+number of terminal exon bp to tolerate in introns     [ default:      8 ]
+.TP
+\fB\-\-max\-bundle\-length\fR
+maximum genomic length allowed for a given bundle     [ default:3500000 ]
+.TP
+\fB\-\-min\-intron\-length\fR
+minimum intron size allowed in genome                 [ default:     50 ]
+.TP
+\fB\-\-trim\-3\-avgcov\-thresh\fR
+minimum avg coverage required to attempt 3' trimming  [ default:     10 ]
+.TP
+\fB\-\-trim\-3\-dropoff\-frac\fR
+fraction of avg coverage below which to trim 3' end   [ default:    0.1 ]
+.SS "Advanced Reference Annotation Guided Assembly Options:"
+.TP
+\fB\-\-no\-faux\-reads\fR
+disable tiling by faux reads                          [ default:  FALSE ]
+.TP
+\fB\-\-3\-overhang\-tolerance\fR
+overhang allowed on 3' end when merging with reference[ default:    600 ]
+.TP
+\fB\-\-intron\-overhang\-tolerance\fR
+overhang allowed inside reference intron when merging [ default:     30 ]
+.SS "Advanced Program Behavior Options:"
+.TP
+\fB\-v\fR/\-\-verbose
+log\-friendly verbose processing (no progress bar)     [ default:  FALSE ]
+.TP
+\fB\-q\fR/\-\-quiet
+log\-friendly quiet processing (no progress bar)       [ default:  FALSE ]
+.TP
+\fB\-\-no\-update\-check\fR
+do not contact server to check for update availability[ default:  FALSE ]
+.SS "Supported library types:"
+.IP
+ff\-firststrand
+ff\-secondstrand
+ff\-unstranded
+fr\-firststrand
+fr\-secondstrand
+fr\-unstranded (default)
+transfrags
+.PP
+cufflinks v1.0.2
+linked against Boost version 104601
+\fB\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\fR
+Usage:   cufflinks [options] <hits.sam>
+General Options:
+.TP
+\fB\-o\fR/\-\-output\-dir
+write all output files to this directory              [ default:     ./ ]
+.TP
+\fB\-p\fR/\-\-num\-threads
+number of threads used during analysis                [ default:      1 ]
+.TP
+\fB\-G\fR/\-\-GTF
+quantitate against reference transcript annotations
+.TP
+\fB\-g\fR/\-\-GTF\-guide
+use reference transcript annotation to guide assembly
+.TP
+\fB\-M\fR/\-\-mask\-file
+ignore all alignment within transcripts in this file
+.TP
+\fB\-b\fR/\-\-frag\-bias\-correct
+use bias correction \- reference fasta required        [ default:   NULL ]
+.TP
+\fB\-u\fR/\-\-multi\-read\-correct
+use 'rescue method' for multi\-reads (more accurate)   [ default:  FALSE ]
+.TP
+\fB\-\-library\-type\fR
+library prep used for input reads                     [ default:  below ]
+.SS "Advanced Abundance Estimation Options:"
+.TP
+\fB\-m\fR/\-\-frag\-len\-mean
+average fragment length (unpaired reads only)         [ default:    200 ]
+.TP
+\fB\-s\fR/\-\-frag\-len\-std\-dev
+fragment length std deviation (unpaired reads only)   [ default:     80 ]
+.TP
+\fB\-\-upper\-quartile\-norm\fR
+use upper\-quartile normalization                      [ default:  FALSE ]
+.TP
+\fB\-\-max\-mle\-iterations\fR
+maximum iterations allowed for MLE calculation        [ default:   5000 ]
+.TP
+\fB\-\-num\-importance\-samples\fR
+number of importance samples for MAP restimation      [ default:   1000 ]
+.TP
+\fB\-\-compatible\-hits\-norm\fR
+count hits compatible with reference RNAs only        [ default:  FALSE ]
+.TP
+\fB\-\-total\-hits\-norm\fR
+count all hits for normalization                      [ default:  TRUE  ]
+.SS "Advanced Assembly Options:"
+.TP
+\fB\-L\fR/\-\-label
+assembled transcripts have this ID prefix             [ default:   CUFF ]
+.TP
+\fB\-F\fR/\-\-min\-isoform\-fraction
+suppress transcripts below this abundance level       [ default:   0.10 ]
+.TP
+\fB\-j\fR/\-\-pre\-mrna\-fraction
+suppress intra\-intronic transcripts below this level  [ default:   0.15 ]
+.TP
+\fB\-I\fR/\-\-max\-intron\-length
+ignore alignments with gaps longer than this          [ default: 300000 ]
+.TP
+\fB\-a\fR/\-\-junc\-alpha
+alpha for junction binomial test filter               [ default:  0.001 ]
+.TP
+\fB\-A\fR/\-\-small\-anchor\-fraction
+percent read overhang taken as 'suspiciously small'   [ default:   0.09 ]
+.TP
+\fB\-\-min\-frags\-per\-transfrag\fR
+minimum number of fragments needed for new transfrags [ default:     10 ]
+.TP
+\fB\-\-overhang\-tolerance\fR
+number of terminal exon bp to tolerate in introns     [ default:      8 ]
+.TP
+\fB\-\-max\-bundle\-length\fR
+maximum genomic length allowed for a given bundle     [ default:3500000 ]
+.TP
+\fB\-\-min\-intron\-length\fR
+minimum intron size allowed in genome                 [ default:     50 ]
+.TP
+\fB\-\-trim\-3\-avgcov\-thresh\fR
+minimum avg coverage required to attempt 3' trimming  [ default:     10 ]
+.TP
+\fB\-\-trim\-3\-dropoff\-frac\fR
+fraction of avg coverage below which to trim 3' end   [ default:    0.1 ]
+.SS "Advanced Reference Annotation Guided Assembly Options:"
+.TP
+\fB\-\-no\-faux\-reads\fR
+disable tiling by faux reads                          [ default:  FALSE ]
+.TP
+\fB\-\-3\-overhang\-tolerance\fR
+overhang allowed on 3' end when merging with reference[ default:    600 ]
+.TP
+\fB\-\-intron\-overhang\-tolerance\fR
+overhang allowed inside reference intron when merging [ default:     30 ]
+.SS "Advanced Program Behavior Options:"
+.TP
+\fB\-v\fR/\-\-verbose
+log\-friendly verbose processing (no progress bar)     [ default:  FALSE ]
+.TP
+\fB\-q\fR/\-\-quiet
+log\-friendly quiet processing (no progress bar)       [ default:  FALSE ]
+.TP
+\fB\-\-no\-update\-check\fR
+do not contact server to check for update availability[ default:  FALSE ]
+.SS "Supported library types:"
+.IP
+ff\-firststrand
+ff\-secondstrand
+ff\-unstranded
+fr\-firststrand
+fr\-secondstrand
+fr\-unstranded (default)
+transfrags
diff --git a/debian/cufflinks.manpages b/debian/cufflinks.manpages
new file mode 100644
index 0000000..0f65186
--- /dev/null
+++ b/debian/cufflinks.manpages
@@ -0,0 +1 @@
+debian/*.1
diff --git a/debian/cuffmerge.1 b/debian/cuffmerge.1
new file mode 100644
index 0000000..c62b971
--- /dev/null
+++ b/debian/cuffmerge.1
@@ -0,0 +1,33 @@
+.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.39.4.
+.TH CUFFMERGE "1" "May 2011" "merge_cuff_asms v1.0.0" "User Commands"
+.SH NAME
+cuffmerge \- Merging assemblies
+.SH DESCRIPTION
+cuffmerge takes two or more Cufflinks GTF files and merges them into a
+single unified transcript catalog.  Optionally, you can provide the script
+with a reference GTF, and the script will use it to attach gene names and other
+metadata to the merged catalog.
+.SH SYNOPSIS
+cuffmerge [Options] <assembly_GTF_list.txt>
+.SH OPTIONS
+.TP
+\fB\-h\fR/\-\-help
+Prints the help message and exits
+.TP
+\fB\-o\fR
+<output_dir>     Directory where merged assembly will be written  [ default: ./merged_asm  ]
+.TP
+\fB\-g\fR/\-\-ref\-gtf
+An optional "reference" annotation GTF.
+.TP
+\fB\-s\fR/\-\-ref\-sequence
+<seq_dir>/<seq_fasta> Genomic DNA sequences for the reference.
+.TP
+\fB\-\-min\-isoform\-fraction\fR <0\-1.0>
+Discard isoforms with abundance below this       [ default: 0.5 ]
+.TP
+\fB\-p\fR/\-\-num\-threads
+<int>            Use this many threads to merge assemblies.       [ default: 1 ]
+.TP
+\fB\-\-keep\-tmp\fR
+Keep all intermediate files during merge
diff --git a/debian/gffread.1 b/debian/gffread.1
new file mode 100644
index 0000000..5499117
--- /dev/null
+++ b/debian/gffread.1
@@ -0,0 +1,219 @@
+.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.39.4.
+.TH GFFREAD "1" "May 2011" "gffread Usage:" "User Commands"
+.SH NAME
+gffread \- one of the cufflinks tools
+.SH SYSNOPSIS
+gffread <input_gff> [\-g <genomic_seqs_fasta> | <dir>][\-s <seq_info.fsize>]
+.IP
+[\-o <outfile.gff>] [\-t <tname>] [\-r [[<strand>]<chr>:]<start>..<end>]
+[\-CTVNMAFGRUVBHSZWTOE] [\-w <spl_exons.fa>] [\-x <spl_cds.fa>] [\-y <tr_cds.fa>]
+[\-i <maxintron>]
+Filters and/or converts GFF3/GTF2 records.
+<input_gff> is a GFF file, use '\-' if the GFF records will be given at stdin
+.IP
+.SH Options
+.TP
+\fB\-g\fR
+full path to a multi\-fasta file with the genomic sequences
+for all input mappings, OR a directory with single\-fasta files
+(one per genomic sequence, with file names matching sequence names)
+.TP
+\fB\-s\fR
+<seq_info.fsize> is a tab\-delimited file providing this info
+for each of the mapped sequences:
+<seq\-name> <seq\-length> <seq\-description>
+(useful for mRNA/EST/protein mappings with \fB\-A\fR option)
+.TP
+\fB\-i\fR
+discard transcripts having an intron larger than <maxintron>
+.TP
+\fB\-r\fR
+only show transcripts crossing coordinate range <start>..<end>
+(on chromosome/contig <chr>, strand <strand> if provided)
+.TP
+\fB\-R\fR
+for \fB\-r\fR option, discard all transcripts that are not fully
+contained within given range
+.TP
+\fB\-U\fR
+discard single\-exon transcripts
+.TP
+\fB\-C\fR
+discard mRNAs that have no CDS feature
+.TP
+\fB\-F\fR
+keep all attributes from last column of GFF/GTF
+.TP
+\fB\-G\fR
+only parse additional exon attributes from the first exon
+and move them to the mRNA level (useful for GTF input)
+.TP
+\fB\-A\fR
+use the description field from <seq_info.fsize> and add it
+as the value for a 'descr' attribute to the GFF record
+.TP
+\fB\-O\fR
+process non\-transcript GFF records as well (by default non\-transcript      records are ignored).
+.TP
+\fB\-V\fR
+discard any mRNAs with CDS having in\-frame stop codons
+.TP
+\fB\-H\fR
+for \fB\-V\fR option, check and adjust the starting CDS phase
+if the original phase leads to a translation with an
+in\-frame stop codon
+.TP
+\fB\-B\fR
+for \fB\-V\fR option, single\-exon transcripts are also checked on the
+opposite strand
+.TP
+\fB\-N\fR
+only show multi\-exon mRNAs if all their introns have the
+typical splice site consensus ( GT\-AG, GC\-AG or AT\-AC )
+.TP
+\fB\-M\fR
+discard any mRNAs that either lack initial START codon
+or the terminal STOP codon, or have an in\-frame stop codon
+(only print mRNAs with a fulll, valid CDS)
+.TP
+\fB\-E\fR
+expose (warn about) duplicate transcript IDs and other potential
+problems with the input GFF/GTF records
+.TP
+\fB\-S\fR
+sort output GFF records by genomic sequence and start coordinate
+(this option is automatically enabled by \fB\-g\fR option)
+.TP
+\fB\-Z\fR
+merge close exons into a single exon (for intron size<4)
+.TP
+\fB\-w\fR
+write a fasta file with spliced exons for each GFF transcript
+.TP
+\fB\-x\fR
+write a fasta file with spliced CDS for each GFF transcript
+.TP
+\fB\-W\fR
+for \fB\-w\fR and \fB\-x\fR options, also write for each fasta record the exon
+coordinates projected onto the spliced sequence
+.TP
+\fB\-y\fR
+write a protein fasta file with the translation of CDS for each record
+.TP
+\fB\-o\fR
+the "filtered" GFF records will be written to <outfile.gff>
+(use \fB\-o\-\fR for printing to stdout)
+.TP
+\fB\-t\fR
+use <trackname> in the second column of each GFF output line
+.HP
+\fB\-T\fR  \fB\-o\fR option will output GTF format instead of GFF3
+.PP
+Invalid argument: \fB\-\-help\fR
+.PP
+gffread <input_gff> [\-g <genomic_seqs_fasta> | <dir>][\-s <seq_info.fsize>]
+.IP
+[\-o <outfile.gff>] [\-t <tname>] [\-r [[<strand>]<chr>:]<start>..<end>]
+[\-CTVNMAFGRUVBHSZWTOE] [\-w <spl_exons.fa>] [\-x <spl_cds.fa>] [\-y <tr_cds.fa>]
+[\-i <maxintron>]
+Filters and/or converts GFF3/GTF2 records.
+<input_gff> is a GFF file, use '\-' if the GFF records will be given at stdin
+.IP
+Options:
+.TP
+\fB\-g\fR
+full path to a multi\-fasta file with the genomic sequences
+for all input mappings, OR a directory with single\-fasta files
+(one per genomic sequence, with file names matching sequence names)
+.TP
+\fB\-s\fR
+<seq_info.fsize> is a tab\-delimited file providing this info
+for each of the mapped sequences:
+<seq\-name> <seq\-length> <seq\-description>
+(useful for mRNA/EST/protein mappings with \fB\-A\fR option)
+.TP
+\fB\-i\fR
+discard transcripts having an intron larger than <maxintron>
+.TP
+\fB\-r\fR
+only show transcripts crossing coordinate range <start>..<end>
+(on chromosome/contig <chr>, strand <strand> if provided)
+.TP
+\fB\-R\fR
+for \fB\-r\fR option, discard all transcripts that are not fully
+contained within given range
+.TP
+\fB\-U\fR
+discard single\-exon transcripts
+.TP
+\fB\-C\fR
+discard mRNAs that have no CDS feature
+.TP
+\fB\-F\fR
+keep all attributes from last column of GFF/GTF
+.TP
+\fB\-G\fR
+only parse additional exon attributes from the first exon
+and move them to the mRNA level (useful for GTF input)
+.TP
+\fB\-A\fR
+use the description field from <seq_info.fsize> and add it
+as the value for a 'descr' attribute to the GFF record
+.TP
+\fB\-O\fR
+process non\-transcript GFF records as well (by default non\-transcript      records are ignored).
+.TP
+\fB\-V\fR
+discard any mRNAs with CDS having in\-frame stop codons
+.TP
+\fB\-H\fR
+for \fB\-V\fR option, check and adjust the starting CDS phase
+if the original phase leads to a translation with an
+in\-frame stop codon
+.TP
+\fB\-B\fR
+for \fB\-V\fR option, single\-exon transcripts are also checked on the
+opposite strand
+.TP
+\fB\-N\fR
+only show multi\-exon mRNAs if all their introns have the
+typical splice site consensus ( GT\-AG, GC\-AG or AT\-AC )
+.TP
+\fB\-M\fR
+discard any mRNAs that either lack initial START codon
+or the terminal STOP codon, or have an in\-frame stop codon
+(only print mRNAs with a fulll, valid CDS)
+.TP
+\fB\-E\fR
+expose (warn about) duplicate transcript IDs and other potential
+problems with the input GFF/GTF records
+.TP
+\fB\-S\fR
+sort output GFF records by genomic sequence and start coordinate
+(this option is automatically enabled by \fB\-g\fR option)
+.TP
+\fB\-Z\fR
+merge close exons into a single exon (for intron size<4)
+.TP
+\fB\-w\fR
+write a fasta file with spliced exons for each GFF transcript
+.TP
+\fB\-x\fR
+write a fasta file with spliced CDS for each GFF transcript
+.TP
+\fB\-W\fR
+for \fB\-w\fR and \fB\-x\fR options, also write for each fasta record the exon
+coordinates projected onto the spliced sequence
+.TP
+\fB\-y\fR
+write a protein fasta file with the translation of CDS for each record
+.TP
+\fB\-o\fR
+the "filtered" GFF records will be written to <outfile.gff>
+(use \fB\-o\-\fR for printing to stdout)
+.TP
+\fB\-t\fR
+use <trackname> in the second column of each GFF output line
+.HP
+\fB\-T\fR  \fB\-o\fR option will output GTF format instead of GFF3
+.PP
diff --git a/debian/gtf_to_sam.1 b/debian/gtf_to_sam.1
new file mode 100644
index 0000000..1dfe568
--- /dev/null
+++ b/debian/gtf_to_sam.1
@@ -0,0 +1,14 @@
+.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.39.4.
+.TH GTF_TO_SAM: "1" "May 2011" "gtf_to_sam v1.0.2" "User Commands"
+.SH NAME
+gtf_to_sam: \- GTF_TO_SAM
+.SH SYNOPSIS
+.B cufflinks
+[\fIoptions\fR] \fI<transcripts1.gtf,\fR...\fI,transcriptsN.gtf> <out.sam>\fR
+gtf_to_sam v1.0.2
+.SH OPTIONS
+\fB\-r\fR/\-\-reference\-seq                        reference fasta file                     [ default:   NULL ]
+
+\fB\-F\fR/\-\-raw\-fpkm                         use FPKM instead of isoform fraction
+.PP
+.SH AUTHOR
diff --git a/debian/patches/configure.patch b/debian/patches/configure.patch
deleted file mode 100644
index 1df4d58..0000000
--- a/debian/patches/configure.patch
+++ /dev/null
@@ -1,14 +0,0 @@
-Description: changing configure script so it looks for bam.h in /usr/includes
-Author: Alex Mestiashvili <alex at biotec.tu-dresden.de>
-Last-Update: 2011-05-05
---- cufflinks-0.9.3.orig/configure
-+++ cufflinks-0.9.3/configure
-@@ -4014,7 +4014,7 @@
- cat >>conftest.$ac_ext <<_ACEOF
- /* end confdefs.h.  */
- 
--	#include <bam/bam.h>
-+	#include <bam.h>
- 
- int
- main ()
diff --git a/debian/patches/fix_configure.patch b/debian/patches/fix_configure.patch
new file mode 100644
index 0000000..ae191cd
--- /dev/null
+++ b/debian/patches/fix_configure.patch
@@ -0,0 +1,14 @@
+Description: force configure script to looks for bam.h in /usr/include/samtools
+Author: Alex Mestiashvili <alex at biotec.tu-dresden.de>
+Last-Update: 2011-05-24
+--- cufflinks-1.0.2.orig/configure
++++ cufflinks-1.0.2/configure
+@@ -4206,7 +4206,7 @@
+ cat >>conftest.$ac_ext <<_ACEOF
+ /* end confdefs.h.  */
+ 
+-	#include <bam/bam.h>
++	#include <samtools/bam.h>
+ 
+ int
+ main ()
diff --git a/debian/patches/fix_includes_path.patch b/debian/patches/fix_includes_path.patch
index 28da6da..dafde04 100644
--- a/debian/patches/fix_includes_path.patch
+++ b/debian/patches/fix_includes_path.patch
@@ -1,14 +1,14 @@
-Description: changing header file location so it looks for sam.h in /usr/includes
+Description: sam.h is located in /usr/include/samtools
 Author: Alex Mestiashvili <alex at biotec.tu-dresden.de>
-Last-Update: 2011-05-05
---- cufflinks-0.9.3.orig/src/hits.h
-+++ cufflinks-0.9.3/src/hits.h
+Last-Update: 2011-05-24
+--- cufflinks-1.0.2.orig/src/hits.h
++++ cufflinks-1.0.2/src/hits.h
 @@ -16,7 +16,7 @@
  
  #include <boost/shared_ptr.hpp>
  
 -#include <bam/sam.h>
-+#include <sam.h>
++#include <samtools/sam.h>
  
  #include "common.h"
- 
+ #include "multireads.h"
diff --git a/debian/patches/series b/debian/patches/series
index 207657b..08ef98b 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,2 +1,2 @@
-configure.patch
 fix_includes_path.patch
+fix_configure.patch
diff --git a/debian/watch b/debian/watch
index 9a2c52e..5e77999 100644
--- a/debian/watch
+++ b/debian/watch
@@ -1,3 +1,9 @@
+# Example watch control file for uscan
+# Rename this file to "watch" and then you can run the "uscan" command
+# to check for upstream updates and more.
+# See uscan(1) for format
+
+# Compulsory line, this is a version 3 file
 version=3
 
 http://cufflinks.cbcb.umd.edu/downloads/cufflinks-([\d\.]*)\.tar\.gz
diff --git a/make_bin.sh b/make_bin.sh
index 27eae85..3300d83 100755
--- a/make_bin.sh
+++ b/make_bin.sh
@@ -11,7 +11,9 @@ make
 cp src/cufflinks $1
 cp src/cuffcompare $1
 cp src/cuffdiff $1
-
+cp src/cuffmerge.py $1/cuffmerge
+cp src/gffread $1
+cp src/gtf_to_sam $1
 cp README $1
 cp LICENSE $1
 cp AUTHORS $1
diff --git a/src/GArgs.cpp b/src/GArgs.cpp
index 74f2908..85ce900 100644
--- a/src/GArgs.cpp
+++ b/src/GArgs.cpp
@@ -2,12 +2,6 @@
 #include "GArgs.h"
 #include <ctype.h>
 
-#define TRACE 1
-
-
-//GArgs::is_opt="1"; //just to have a non-NULL value for switch testing
-
-
 GArgs::GArgs(int argc, char* const argv[], const char* format, bool nodigitopts) {
    /* format is:
        <letter>[:]    for e.g. p:hT    <-  -p testing -ptesting -h -T
diff --git a/src/GBase.cpp b/src/GBase.cpp
index 91f0d2f..da20b1c 100644
--- a/src/GBase.cpp
+++ b/src/GBase.cpp
@@ -18,6 +18,30 @@ static char msg[4069];
 #endif
 */
 
+
+int saprintf(char **retp, const char *fmt, ...) {
+  va_list argp;
+  int len;
+  char *buf;
+
+  va_start(argp, fmt);
+  len = vsnprintf(NULL, 0, fmt, argp);
+  va_end(argp);
+  GMALLOC(buf, (len + 1));
+  if(buf == NULL)
+    {
+    *retp = NULL;
+    return -1;
+    }
+
+  va_start(argp, fmt);
+  vsnprintf(buf, len+1, fmt, argp);
+  va_end(argp);
+
+  *retp = buf;
+  return len;
+}
+
 //************************* Debug helpers **************************
 // Assert failed routine
 void GAssert(const char* expression, const char* filename, unsigned int lineno){
@@ -47,6 +71,7 @@ void GError(const char* format,...){
   #endif
     exit(1);
   }
+  
 // Warning routine (just print message without exiting)
 void GMessage(const char* format,...){
   va_list arguments;
@@ -441,13 +466,24 @@ char* strifind(char* str,  const char* substr) {
 
 
 // tests if string s has the given prefix
-bool startsWith(char* s, const char* prefix) {
+bool startsWith(const char* s, const char* prefix) {
  if (prefix==NULL || s==NULL) return false;
  int i=0;
  while (prefix[i]!='\0' && prefix[i]==s[i]) i++;
  return (prefix[i]=='\0');
  }
 
+// tests if string s ends with given suffix
+bool endsWith(const char* s, const char* suffix) {
+ if (suffix==NULL || s==NULL) return false;
+ if (suffix[0]==0) return true; //special case: empty suffix
+ int j=strlen(suffix)-1;
+ int i=strlen(s)-1;
+ if (i<j) return false;
+ while (j>=0 && s[i]==suffix[j]) { i--; j--; }
+ return (j==-1);
+ }
+
 
 char* reverseChars(char* str, int slen) {
   if (slen==0) slen=strlen(str);
@@ -494,26 +530,38 @@ int strhash(const char* str){
   return h;
   }
 
-// removes the directory part from a full-path file name
+// removes the last part (file or directory name) of a full path
 // this is a destructive operation for the given string!!!
 // the trailing '/' is guaranteed to be there
 void delFileName(char* filepath) {
  char *p, *sep;
  if (filepath==NULL) return;
  for (p=filepath, sep=filepath;*p!='\0';p++)
-     if (*p==CHPATHSEP) sep=p+1;
+     if (*p=='/' || *p=='\\') sep=p+1;
  *sep='\0'; // truncate filepath
 }
 
-// returns a pointer to the file name part in a full-path filename
-char* getFileName(char* filepath) {
- char *p, *sep;
+// returns a pointer to the last file or directory name in a full path
+const char* getFileName(const char* filepath) {
+ const char *p, *sep;
  if (filepath==NULL) return NULL;
  for (p=filepath, sep=filepath;*p!='\0';p++)
-     if (*p==CHPATHSEP) sep=p+1;
+     if (*p=='/' || *p=='\\') sep=p+1;
  return sep;
 }
 
+// returns a pointer to the file "extension" part in a filename
+const char* getFileExt(const char* filepath) {
+ const char *p, *dp, *sep;
+ if (filepath==NULL) return NULL;
+ for (p=filepath, dp=filepath, sep=filepath;*p!='\0';p++) {
+     if (*p=='.') dp=p+1;
+       else if (*p=='/' || *p=='\\') 
+                  sep=p+1;
+     }
+ return (dp>sep) ? dp : NULL ;
+}
+
 int fileExists(const char* fname) {
   struct stat stFileInfo;
   int r=0;
@@ -678,3 +726,28 @@ void writeFasta(FILE *fw, const char* seqid, const char* descr,
   fflush(fw);
  }
 
+char* commaprint(uint64 n) {
+  static int comma = '\0';
+  static char retbuf[48];
+  char *p = &retbuf[sizeof(retbuf)-1];
+  int i = 0;
+  if(comma == '\0') {
+    /* struct lconv *lcp = localeconv();
+    if(lcp != NULL) {
+      if(lcp->thousands_sep != NULL &&
+        *lcp->thousands_sep != '\0')
+        comma = *lcp->thousands_sep;
+      else  */
+                          comma = ',';
+     // }
+    }
+  *p = '\0';
+  do {
+    if(i%3 == 0 && i != 0)
+      *--p = comma;
+    *--p = '0' + n % 10;
+    n /= 10;
+    i++;
+  } while(n != 0);
+  return p;
+}
diff --git a/src/GBase.h b/src/GBase.h
index 5833341..9df7325 100644
--- a/src/GBase.h
+++ b/src/GBase.h
@@ -150,7 +150,6 @@ inline char* strMax(char *arg1, char *arg2) {
     return (strcmp(arg2, arg1) < 0)? arg1 : arg2;
 }
 
-
 inline int iround(double x) {
    return (int)floor(x + 0.5);
 }
@@ -223,14 +222,14 @@ bool GCalloc(pointer* ptr, unsigned long size); // Allocate and initialize memor
 bool GRealloc(pointer* ptr,unsigned long size); // Resize memory
 void GFree(pointer* ptr); // Free memory, resets ptr to NULL
 
-/********************* debug functions *********************/
+
+int saprintf(char **retp, const char *fmt, ...);
 
 void GError(const char* format,...); // Error routine (aborts program)
 void GMessage(const char* format,...);// Log message to stderr
 // Assert failed routine:- usually not called directly but through GASSERT
 void GAssert(const char* expression, const char* filename, unsigned int lineno);
 
-
 // ****************** string manipulation *************************
 char *Gstrdup(const char* str);
 //duplicate a string by allocating a copy for it and returning it
@@ -287,7 +286,11 @@ char* strifind(char* str,  const char* substr);
 //Determines if a string begins with a given prefix
 //(returns false when any of the params is NULL,
 // but true when prefix is '' (empty string)!)
-bool startsWith(char* s, const char* prefix);
+bool startsWith(const char* s, const char* prefix);
+
+bool endsWith(const char* s, const char* suffix);
+//Note: returns true if suffix is empty string, but false if it's NULL
+
 
 // ELF hash function for strings
 int strhash(const char* str);
@@ -440,14 +443,21 @@ class GLineReader {
   */
 char* fgetline(char* & buf, int& buflen, FILE* stream, off_t* f_pos=NULL, int* linelen=NULL);
 
+
+//print int/values nicely formatted in 3-digit groups
+char* commaprint(uint64 n);
+
 /*********************** File management functions *********************/
 
-// removes the directory part from a full-path file name
-// this is a destructive operation for the given string!
+// removes the last part (file or directory name) of a full path
+// WARNING: this is a destructive operation for the given string!
 void delFileName(char* filepath);
 
-// returns a pointer to the file name part in a full-path filename
-char* getFileName(char* filepath);
+// returns a pointer to the last file or directory name in a full path
+const char* getFileName(const char* filepath);
+// returns a pointer to the file "extension" part in a filename
+const char* getFileExt(const char* filepath);
+
 
 int fileExists(const char* fname);
 //returns 0 if file entry doesn't exist
diff --git a/src/GFaSeqGet.cpp b/src/GFaSeqGet.cpp
index 055af94..6070667 100644
--- a/src/GFaSeqGet.cpp
+++ b/src/GFaSeqGet.cpp
@@ -247,7 +247,6 @@ char* GFaSeqGet::copyRange(uint cstart, uint cend, bool revCmpl, bool upCase) {
 const char* GFaSeqGet::loadsubseq(uint cstart, int& clen) {
   //assumes enough lastsub->sq space allocated previously
   //only loads the requested clen chars from file, at offset &lastsub->sq[cstart-lastsub->sqstart]
-  //DEBUG:
   int sofs=cstart-lastsub->sqstart;
   int lendlen=line_blen-line_len;
   char* seqp=lastsub->sq+sofs;
diff --git a/src/GList.hh b/src/GList.hh
index 33b3993..30fe628 100644
--- a/src/GList.hh
+++ b/src/GList.hh
@@ -45,7 +45,7 @@ template <class OBJ> class GVec {
     int fCount;
     int fCapacity;
   public:
-    GVec(int init_capacity=50);
+    GVec(int init_capacity=20);
     GVec(GVec<OBJ>& array); //copy constructor
     const GVec<OBJ>& operator=(GVec& array); //copy operator
     virtual ~GVec();
@@ -297,9 +297,17 @@ template <class OBJ> class GStack {
 
 //-------------------- TEMPLATE IMPLEMENTATION-------------------------------
 
+template <class OBJ> GVec<OBJ>::GVec(int init_capacity) {
+  fCount=0;
+  fCapacity=0;
+  fArray=NULL;
+  setCapacity(init_capacity);
+}
+
 template <class OBJ> GVec<OBJ>::GVec(GVec<OBJ>& array) { //copy constructor
  this->fCount=array.fCount;
  this->fCapacity=array.fCapacity;
+ this->fArray=NULL;
  if (this->fCapacity>0) {
     GMALLOC(fArray, fCapacity*sizeof(OBJ));
     }
@@ -311,6 +319,7 @@ template <class OBJ> GVec<OBJ>::GVec(GVec<OBJ>& array) { //copy constructor
 template <class OBJ> GArray<OBJ>::GArray(GArray<OBJ>& array):GVec<OBJ>(0) { //copy constructor
  this->fCount=array.fCount;
  this->fCapacity=array.fCapacity;
+ this->fArray=NULL;
  if (this->fCapacity>0) {
     GMALLOC(this->fArray, this->fCapacity*sizeof(OBJ));
     }
@@ -341,12 +350,12 @@ template <class OBJ> const GArray<OBJ>& GArray<OBJ>::operator=(GArray<OBJ>& arra
  if (&array==this) return *this;
  GVec<OBJ>::Clear();
  this->fCount=array.fCount;
- fUnique=array.fUnique;
+ this->fUnique=array.fUnique;
  this->fCapacity=array.fCapacity;
  if (this->fCapacity>0) {
     GMALLOC(this->fArray, this->fCapacity*sizeof(OBJ));
     }
- fCompareProc=array.fCompareProc;
+ this->fCompareProc=array.fCompareProc;
  this->fCount=array.fCount;
  // uses OBJ operator=
  for (int i=0;i<this->fCount;i++) {
@@ -365,13 +374,6 @@ template <class OBJ> GArray<OBJ>::GArray(bool sorted, bool unique):GVec<OBJ>(0)
   fCompareProc=sorted? &DefaultCompareProc : NULL;
 }
 
-template <class OBJ> GVec<OBJ>::GVec(int init_capacity) {
-  fCount=0;
-  fCapacity=0;
-  fArray=NULL;
-  setCapacity(init_capacity);
-}
-
 template <class OBJ> GArray<OBJ>::GArray(int init_capacity,
                         bool sorted, bool unique):GVec<OBJ>(init_capacity) {
   fUnique=unique;
@@ -504,7 +506,7 @@ template <class OBJ> int GArray<OBJ>::Add(OBJ* item) {
    if (Found(*item, result))
       if (fUnique) return -1; //cannot add a duplicate!
    //Found sets result to the position where the item should be!
-   idxInsert(result, *item);
+   this->idxInsert(result, *item);
    }
   else {
    if (fUnique && Found(*item,result)) return -1; //set behaviour
@@ -534,7 +536,7 @@ template <class OBJ> void GArray<OBJ>::Add(GArray<OBJ>& list) {
     for (int i=0;i<list.fCount;i++) Add(&list[i]);
     }
   else { //simply copy
-    setCapacity(this->fCapacity+list.fCount);
+    this->setCapacity(this->fCapacity+list.fCount);
     int s=this->fCount;
     for (int i=0;i<list.fCount;i++)
            this->fArray[s+i]=list.fArray[i];
@@ -715,6 +717,7 @@ template <class OBJ> GPVec<OBJ>::GPVec(GPVec& list) { //copy constructor
 template <class OBJ> GPVec<OBJ>::GPVec(GPVec* plist) { //another copy constructor
  fCount=0;
  fCapacity=plist->fCapacity;
+ fList=NULL;
  if (fCapacity>0) {
      GMALLOC(fList, fCapacity*sizeof(OBJ*));
      }
@@ -752,6 +755,7 @@ template <class OBJ> GList<OBJ>::GList(GList<OBJ>& list):GPVec<OBJ>(list) { //co
 
 template <class OBJ> GList<OBJ>::GList(GList<OBJ>* plist):GPVec<OBJ>(0) { //another copy constructor
  this->fCapacity=plist->fCapacity;
+ this->fList=NULL;
  if (this->fCapacity>0) {
      GMALLOC(this->fList, this->fCapacity*sizeof(OBJ*));
      }
@@ -769,7 +773,7 @@ template <class OBJ> void GList<OBJ>::Add(GList<OBJ>& list) {
     for (int i=0;i<list.Count();i++) Add(list[i]);
     }
   else { //simply copy
-    setCapacity(this->fCapacity+list.fCount);
+    this->setCapacity(this->fCapacity+list.fCount);
     memcpy( & (this->fList[this->fCount]), list.fList, list.fCount*sizeof(OBJ*));
     this->fCount+=list.fCount;
     }
@@ -1235,7 +1239,7 @@ return -1; //not found
 
 template <class OBJ> void GPVec<OBJ>::Pack()  {//also frees items!
  for (int i=fCount-1; i>=0; i--)
-    if (fList[i]==NULL) Delete(i); //also shift contents of fList accordingly
+    if (fList[i]==NULL) Delete(i); //shift rest of fList content accordingly
 }
 
 template <class OBJ> void GPVec<OBJ>::setCount(int NewCount) {
diff --git a/src/GStr.cpp b/src/GStr.cpp
index d70a8c3..f79f6be 100644
--- a/src/GStr.cpp
+++ b/src/GStr.cpp
@@ -331,7 +331,23 @@ bool GStr::contains(const char *s) const {
  }
 
 bool GStr::startsWith(const char *s) const {
- return (index(s, 0) == 0);
+ //return (index(s, 0) == 0);
+ return ::startsWith(this->chars(), s);
+ }
+
+bool GStr::startsWith(const GStr& s) const {
+ //return (index(s, 0) == 0);
+ return ::startsWith(this->chars(), s.chars());
+ }
+
+bool GStr::endsWith(const char *s) const {
+ //return (index(s, 0) == 0);
+ return ::endsWith(this->chars(), s);
+ }
+
+bool GStr::endsWith(const GStr& s) const {
+ //return (index(s, 0) == 0);
+ return ::endsWith(this->chars(), s.chars());
  }
 
 bool GStr::contains(char c) const {
diff --git a/src/GStr.h b/src/GStr.h
index 12e2390..b0bc8ea 100644
--- a/src/GStr.h
+++ b/src/GStr.h
@@ -113,6 +113,9 @@ class GStr {
         bool contains(const char* s) const;
         bool contains(char c) const;
         bool startsWith(const char* s) const;
+        bool startsWith(const GStr& s) const;
+        bool endsWith(const char* s) const;
+        bool endsWith(const GStr& s) const;
         GStr split(const char* delim);
         GStr split(char c);
            /* splits "this" in two parts, at the first (leftmost)
diff --git a/src/Makefile.am b/src/Makefile.am
index e7144cb..30b72df 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,10 @@
+#include $(top_srcdir)/build-aux/cufflinks.mk
+
 AM_CPPFLAGS = -I$(top_srcdir)/src 
 
-EXTRA_DIST = $(top_srcdir)/AUTHORS $(top_srcdir)/make_bin.sh
+EXTRA_DIST = \
+    cuffmerge.py \
+    $(top_srcdir)/AUTHORS $(top_srcdir)/make_bin.sh
 
 AM_CXXFLAGS =
 
@@ -8,12 +12,15 @@ bin_PROGRAMS = \
 	cufflinks \
 	cuffcompare \
 	cuffdiff \
-    gtf_to_sam 
-#    sorting_hat
+    gtf_to_sam \
+    compress_gtf \
+    gffread
+#    cuffcluster \
 #	gtf_reads
 
 noinst_HEADERS = \
-    biascorrection.h \
+	update_check.h \
+	biascorrection.h \
 	codons.h \
 	clustering.h \
 	differential.h \
@@ -81,6 +88,17 @@ noinst_HEADERS = \
 	lemon/concepts/maps.h \
 	lemon/concepts/path.h \
 	lemon/concepts/matrix_maps.h \
+    locfit/design.h	\
+    locfit/lfcons.h	\
+    locfit/lfstruc.h \
+    locfit/local.h \
+    locfit/imatlb.h	\
+    locfit/lffuns.h	\
+    locfit/lfwin.h \
+    locfit/mutil.h \
+    locfit/vari.hpp \
+    replicates.h \
+    multireads.h \
 	common.h 
 
 noinst_LIBRARIES = libcufflinks.a libgc.a
@@ -101,6 +119,59 @@ libcufflinks_a_SOURCES = \
     matching_merge.cpp \
     graph_optimize.cpp \
     biascorrection.cpp \
+    locfit/adap.c \
+    locfit/ar_funs.c \
+    locfit/arith.c \
+    locfit/band.c \
+    locfit/c_args.c \
+    locfit/c_plot.c \
+    locfit/cmd.c \
+    locfit/dens_haz.c \
+    locfit/dens_int.c \
+    locfit/dens_odi.c \
+    locfit/density.c \
+    locfit/dist.c \
+    locfit/ev_atree.c \
+    locfit/ev_interp.c \
+    locfit/ev_kdtre.c \
+    locfit/ev_main.c \
+    locfit/ev_trian.c \
+    locfit/family.c \
+    locfit/fitted.c \
+    locfit/frend.c \
+    locfit/help.c \
+    locfit/lf_dercor.c \
+    locfit/lf_fitfun.c \
+    locfit/lf_robust.c \
+    locfit/lf_vari.c \
+    locfit/lfd.c \
+    locfit/lfstr.c \
+    locfit/linalg.c \
+    locfit/locfit.c \
+    locfit/m_chol.c \
+    locfit/m_eigen.c \
+    locfit/m_jacob.c \
+    locfit/m_max.c \
+    locfit/makecmd.c \
+    locfit/math.c \
+    locfit/minmax.c \
+    locfit/nbhd.c \
+    locfit/pcomp.c \
+    locfit/pout.c \
+    locfit/preplot.c \
+    locfit/random.c \
+    locfit/readfile.c \
+    locfit/scb.c \
+    locfit/scb_cons.c \
+    locfit/simul.c \
+    locfit/solve.c \
+    locfit/startlf.c \
+    locfit/strings.c \
+    locfit/vari.cpp \
+    locfit/wdiag.c \
+    locfit/weight.c \
+    replicates.cpp \
+    multireads.cpp \
     jensen_shannon.cpp 
 	
 libgc_a_SOURCES = \
@@ -114,6 +185,16 @@ libgc_a_SOURCES = \
 	gff.cpp \
 	gtf_tracking.cpp 
 
+#-- scripts to be installed in $prefix/bin
+bin_SCRIPTS = \
+	cuffmerge
+
+CLEANFILES = $(bin_SCRIPTS)
+
+SUFFIXES = .py
+.py:
+	(echo '#!$(PYTHON)'; sed '/^#!/d' $<) > $@
+
 cufflinks_SOURCES = cufflinks.cpp  
 cufflinks_LDADD = libcufflinks.a libgc.a $(BOOST_THREAD_LIB) $(BAM_LIB) 
 cufflinks_LDFLAGS =  $(BOOST_LDFLAGS) $(BAM_LDFLAGS) #$(ZLIB_LDFLAGS) 
@@ -121,6 +202,9 @@ cufflinks_LDFLAGS =  $(BOOST_LDFLAGS) $(BAM_LDFLAGS) #$(ZLIB_LDFLAGS)
 cuffcompare_SOURCES = cuffcompare.cpp
 cuffcompare_LDADD = libgc.a
 
+gffread_SOURCES = gffread.cpp
+gffread_LDADD = libgc.a
+
 cuffdiff_SOURCES = cuffdiff.cpp
 cuffdiff_LDADD = libcufflinks.a libgc.a $(BOOST_THREAD_LIB) $(BAM_LIB)
 cuffdiff_LDFLAGS =  $(BOOST_LDFLAGS) $(BAM_LDFLAGS) 
@@ -129,9 +213,13 @@ gtf_to_sam_SOURCES = gtf_to_sam.cpp
 gtf_to_sam_LDADD = libcufflinks.a libgc.a $(BOOST_THREAD_LIB) $(BAM_LIB)
 gtf_to_sam_LDFLAGS =  $(BOOST_LDFLAGS) $(BAM_LDFLAGS) 
 
-#sorting_hat_SOURCES = sorting_hat.cpp
-#sorting_hat_LDADD = libcufflinks.a libgc.a $(BOOST_THREAD_LIB) $(BAM_LIB)
-#sorting_hat_LDFLAGS =  $(BOOST_LDFLAGS) $(BAM_LDFLAGS) 
+#cuffcluster_SOURCES = cuffcluster.cpp
+#cuffcluster_LDADD = libcufflinks.a libgc.a $(BOOST_THREAD_LIB) $(BAM_LIB)
+#cuffcluster_LDFLAGS =  $(BOOST_LDFLAGS) $(BAM_LDFLAGS) 
+
+compress_gtf_SOURCES = compress_gtf.cpp
+compress_gtf_LDADD = libcufflinks.a libgc.a $(BOOST_THREAD_LIB) $(BAM_LIB)
+compress_gtf_LDFLAGS =  $(BOOST_LDFLAGS) $(BAM_LDFLAGS) 
 
 #gtf_reads_SOURCES = gtf_reads.cpp
 #gtf_reads_LDADD = libcufflinks.a libgc.a $(BOOST_THREAD_LIB) $(BAM_LIB) 
diff --git a/src/Makefile.in b/src/Makefile.in
index 1f143a8..773ccfe 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -14,6 +14,9 @@
 
 @SET_MAKE@
 
+#include $(top_srcdir)/build-aux/cufflinks.mk
+
+
 
 
 srcdir = @srcdir@
@@ -39,7 +42,8 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 bin_PROGRAMS = cufflinks$(EXEEXT) cuffcompare$(EXEEXT) \
-	cuffdiff$(EXEEXT) gtf_to_sam$(EXEEXT)
+	cuffdiff$(EXEEXT) gtf_to_sam$(EXEEXT) compress_gtf$(EXEEXT) \
+	gffread$(EXEEXT)
 subdir = src
 DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
 	$(srcdir)/Makefile.in
@@ -63,7 +67,25 @@ am_libcufflinks_a_OBJECTS = clustering.$(OBJEXT) \
 	hits.$(OBJEXT) genes.$(OBJEXT) bundles.$(OBJEXT) \
 	filters.$(OBJEXT) scaffold_graph.$(OBJEXT) \
 	matching_merge.$(OBJEXT) graph_optimize.$(OBJEXT) \
-	biascorrection.$(OBJEXT) jensen_shannon.$(OBJEXT)
+	biascorrection.$(OBJEXT) adap.$(OBJEXT) ar_funs.$(OBJEXT) \
+	arith.$(OBJEXT) band.$(OBJEXT) c_args.$(OBJEXT) \
+	c_plot.$(OBJEXT) cmd.$(OBJEXT) dens_haz.$(OBJEXT) \
+	dens_int.$(OBJEXT) dens_odi.$(OBJEXT) density.$(OBJEXT) \
+	dist.$(OBJEXT) ev_atree.$(OBJEXT) ev_interp.$(OBJEXT) \
+	ev_kdtre.$(OBJEXT) ev_main.$(OBJEXT) ev_trian.$(OBJEXT) \
+	family.$(OBJEXT) fitted.$(OBJEXT) frend.$(OBJEXT) \
+	help.$(OBJEXT) lf_dercor.$(OBJEXT) lf_fitfun.$(OBJEXT) \
+	lf_robust.$(OBJEXT) lf_vari.$(OBJEXT) lfd.$(OBJEXT) \
+	lfstr.$(OBJEXT) linalg.$(OBJEXT) locfit.$(OBJEXT) \
+	m_chol.$(OBJEXT) m_eigen.$(OBJEXT) m_jacob.$(OBJEXT) \
+	m_max.$(OBJEXT) makecmd.$(OBJEXT) math.$(OBJEXT) \
+	minmax.$(OBJEXT) nbhd.$(OBJEXT) pcomp.$(OBJEXT) pout.$(OBJEXT) \
+	preplot.$(OBJEXT) random.$(OBJEXT) readfile.$(OBJEXT) \
+	scb.$(OBJEXT) scb_cons.$(OBJEXT) simul.$(OBJEXT) \
+	solve.$(OBJEXT) startlf.$(OBJEXT) strings.$(OBJEXT) \
+	vari.$(OBJEXT) wdiag.$(OBJEXT) weight.$(OBJEXT) \
+	replicates.$(OBJEXT) multireads.$(OBJEXT) \
+	jensen_shannon.$(OBJEXT)
 libcufflinks_a_OBJECTS = $(am_libcufflinks_a_OBJECTS)
 libgc_a_AR = $(AR) $(ARFLAGS)
 libgc_a_LIBADD =
@@ -71,39 +93,54 @@ am_libgc_a_OBJECTS = codons.$(OBJEXT) GArgs.$(OBJEXT) GBase.$(OBJEXT) \
 	gdna.$(OBJEXT) GStr.$(OBJEXT) GFaSeqGet.$(OBJEXT) \
 	GFastaIndex.$(OBJEXT) gff.$(OBJEXT) gtf_tracking.$(OBJEXT)
 libgc_a_OBJECTS = $(am_libgc_a_OBJECTS)
-am__installdirs = "$(DESTDIR)$(bindir)"
+am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)"
 binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
 PROGRAMS = $(bin_PROGRAMS)
+am_compress_gtf_OBJECTS = compress_gtf.$(OBJEXT)
+compress_gtf_OBJECTS = $(am_compress_gtf_OBJECTS)
+am__DEPENDENCIES_1 =
+compress_gtf_DEPENDENCIES = libcufflinks.a libgc.a \
+	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 am_cuffcompare_OBJECTS = cuffcompare.$(OBJEXT)
 cuffcompare_OBJECTS = $(am_cuffcompare_OBJECTS)
 cuffcompare_DEPENDENCIES = libgc.a
 am_cuffdiff_OBJECTS = cuffdiff.$(OBJEXT)
 cuffdiff_OBJECTS = $(am_cuffdiff_OBJECTS)
-am__DEPENDENCIES_1 =
 cuffdiff_DEPENDENCIES = libcufflinks.a libgc.a $(am__DEPENDENCIES_1) \
 	$(am__DEPENDENCIES_1)
 am_cufflinks_OBJECTS = cufflinks.$(OBJEXT)
 cufflinks_OBJECTS = $(am_cufflinks_OBJECTS)
 cufflinks_DEPENDENCIES = libcufflinks.a libgc.a $(am__DEPENDENCIES_1) \
 	$(am__DEPENDENCIES_1)
+am_gffread_OBJECTS = gffread.$(OBJEXT)
+gffread_OBJECTS = $(am_gffread_OBJECTS)
+gffread_DEPENDENCIES = libgc.a
 am_gtf_to_sam_OBJECTS = gtf_to_sam.$(OBJEXT)
 gtf_to_sam_OBJECTS = $(am_gtf_to_sam_OBJECTS)
 gtf_to_sam_DEPENDENCIES = libcufflinks.a libgc.a $(am__DEPENDENCIES_1) \
 	$(am__DEPENDENCIES_1)
+binSCRIPT_INSTALL = $(INSTALL_SCRIPT)
+SCRIPTS = $(bin_SCRIPTS)
 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
 am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
 CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
 	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
 CXXLD = $(CXX)
 CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \
 	-o $@
 SOURCES = $(libcufflinks_a_SOURCES) $(libgc_a_SOURCES) \
-	$(cuffcompare_SOURCES) $(cuffdiff_SOURCES) \
-	$(cufflinks_SOURCES) $(gtf_to_sam_SOURCES)
+	$(compress_gtf_SOURCES) $(cuffcompare_SOURCES) \
+	$(cuffdiff_SOURCES) $(cufflinks_SOURCES) $(gffread_SOURCES) \
+	$(gtf_to_sam_SOURCES)
 DIST_SOURCES = $(libcufflinks_a_SOURCES) $(libgc_a_SOURCES) \
-	$(cuffcompare_SOURCES) $(cuffdiff_SOURCES) \
-	$(cufflinks_SOURCES) $(gtf_to_sam_SOURCES)
+	$(compress_gtf_SOURCES) $(cuffcompare_SOURCES) \
+	$(cuffdiff_SOURCES) $(cufflinks_SOURCES) $(gffread_SOURCES) \
+	$(gtf_to_sam_SOURCES)
 HEADERS = $(noinst_HEADERS)
 ETAGS = etags
 CTAGS = ctags
@@ -155,6 +192,11 @@ PACKAGE_STRING = @PACKAGE_STRING@
 PACKAGE_TARNAME = @PACKAGE_TARNAME@
 PACKAGE_VERSION = @PACKAGE_VERSION@
 PATH_SEPARATOR = @PATH_SEPARATOR@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
 RANLIB = @RANLIB@
 SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
@@ -196,19 +238,27 @@ localstatedir = @localstatedir@
 mandir = @mandir@
 mkdir_p = @mkdir_p@
 oldincludedir = @oldincludedir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 sysconfdir = @sysconfdir@
 target_alias = @target_alias@
 AM_CPPFLAGS = -I$(top_srcdir)/src 
-EXTRA_DIST = $(top_srcdir)/AUTHORS $(top_srcdir)/make_bin.sh
+EXTRA_DIST = \
+    cuffmerge.py \
+    $(top_srcdir)/AUTHORS $(top_srcdir)/make_bin.sh
+
 AM_CXXFLAGS = 
-#    sorting_hat
+#    cuffcluster \
 #	gtf_reads
 noinst_HEADERS = \
-    biascorrection.h \
+	update_check.h \
+	biascorrection.h \
 	codons.h \
 	clustering.h \
 	differential.h \
@@ -276,6 +326,17 @@ noinst_HEADERS = \
 	lemon/concepts/maps.h \
 	lemon/concepts/path.h \
 	lemon/concepts/matrix_maps.h \
+    locfit/design.h	\
+    locfit/lfcons.h	\
+    locfit/lfstruc.h \
+    locfit/local.h \
+    locfit/imatlb.h	\
+    locfit/lffuns.h	\
+    locfit/lfwin.h \
+    locfit/mutil.h \
+    locfit/vari.hpp \
+    replicates.h \
+    multireads.h \
 	common.h 
 
 noinst_LIBRARIES = libcufflinks.a libgc.a
@@ -295,6 +356,59 @@ libcufflinks_a_SOURCES = \
     matching_merge.cpp \
     graph_optimize.cpp \
     biascorrection.cpp \
+    locfit/adap.c \
+    locfit/ar_funs.c \
+    locfit/arith.c \
+    locfit/band.c \
+    locfit/c_args.c \
+    locfit/c_plot.c \
+    locfit/cmd.c \
+    locfit/dens_haz.c \
+    locfit/dens_int.c \
+    locfit/dens_odi.c \
+    locfit/density.c \
+    locfit/dist.c \
+    locfit/ev_atree.c \
+    locfit/ev_interp.c \
+    locfit/ev_kdtre.c \
+    locfit/ev_main.c \
+    locfit/ev_trian.c \
+    locfit/family.c \
+    locfit/fitted.c \
+    locfit/frend.c \
+    locfit/help.c \
+    locfit/lf_dercor.c \
+    locfit/lf_fitfun.c \
+    locfit/lf_robust.c \
+    locfit/lf_vari.c \
+    locfit/lfd.c \
+    locfit/lfstr.c \
+    locfit/linalg.c \
+    locfit/locfit.c \
+    locfit/m_chol.c \
+    locfit/m_eigen.c \
+    locfit/m_jacob.c \
+    locfit/m_max.c \
+    locfit/makecmd.c \
+    locfit/math.c \
+    locfit/minmax.c \
+    locfit/nbhd.c \
+    locfit/pcomp.c \
+    locfit/pout.c \
+    locfit/preplot.c \
+    locfit/random.c \
+    locfit/readfile.c \
+    locfit/scb.c \
+    locfit/scb_cons.c \
+    locfit/simul.c \
+    locfit/solve.c \
+    locfit/startlf.c \
+    locfit/strings.c \
+    locfit/vari.cpp \
+    locfit/wdiag.c \
+    locfit/weight.c \
+    replicates.cpp \
+    multireads.cpp \
     jensen_shannon.cpp 
 
 libgc_a_SOURCES = \
@@ -308,21 +422,37 @@ libgc_a_SOURCES = \
 	gff.cpp \
 	gtf_tracking.cpp 
 
+
+#-- scripts to be installed in $prefix/bin
+bin_SCRIPTS = \
+	cuffmerge
+
+CLEANFILES = $(bin_SCRIPTS)
+SUFFIXES = .py
 cufflinks_SOURCES = cufflinks.cpp  
 cufflinks_LDADD = libcufflinks.a libgc.a $(BOOST_THREAD_LIB) $(BAM_LIB) 
 cufflinks_LDFLAGS = $(BOOST_LDFLAGS) $(BAM_LDFLAGS) #$(ZLIB_LDFLAGS) 
 cuffcompare_SOURCES = cuffcompare.cpp
 cuffcompare_LDADD = libgc.a
+gffread_SOURCES = gffread.cpp
+gffread_LDADD = libgc.a
 cuffdiff_SOURCES = cuffdiff.cpp
 cuffdiff_LDADD = libcufflinks.a libgc.a $(BOOST_THREAD_LIB) $(BAM_LIB)
 cuffdiff_LDFLAGS = $(BOOST_LDFLAGS) $(BAM_LDFLAGS) 
 gtf_to_sam_SOURCES = gtf_to_sam.cpp
 gtf_to_sam_LDADD = libcufflinks.a libgc.a $(BOOST_THREAD_LIB) $(BAM_LIB)
 gtf_to_sam_LDFLAGS = $(BOOST_LDFLAGS) $(BAM_LDFLAGS) 
+
+#cuffcluster_SOURCES = cuffcluster.cpp
+#cuffcluster_LDADD = libcufflinks.a libgc.a $(BOOST_THREAD_LIB) $(BAM_LIB)
+#cuffcluster_LDFLAGS =  $(BOOST_LDFLAGS) $(BAM_LDFLAGS) 
+compress_gtf_SOURCES = compress_gtf.cpp
+compress_gtf_LDADD = libcufflinks.a libgc.a $(BOOST_THREAD_LIB) $(BAM_LIB)
+compress_gtf_LDFLAGS = $(BOOST_LDFLAGS) $(BAM_LDFLAGS) 
 all: all-am
 
 .SUFFIXES:
-.SUFFIXES: .cpp .o .obj
+.SUFFIXES: .py .c .cpp .o .obj
 $(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
 	@for dep in $?; do \
 	  case '$(am__configure_deps)' in \
@@ -386,6 +516,9 @@ uninstall-binPROGRAMS:
 
 clean-binPROGRAMS:
 	-test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+compress_gtf$(EXEEXT): $(compress_gtf_OBJECTS) $(compress_gtf_DEPENDENCIES) 
+	@rm -f compress_gtf$(EXEEXT)
+	$(CXXLINK) $(compress_gtf_LDFLAGS) $(compress_gtf_OBJECTS) $(compress_gtf_LDADD) $(LIBS)
 cuffcompare$(EXEEXT): $(cuffcompare_OBJECTS) $(cuffcompare_DEPENDENCIES) 
 	@rm -f cuffcompare$(EXEEXT)
 	$(CXXLINK) $(cuffcompare_LDFLAGS) $(cuffcompare_OBJECTS) $(cuffcompare_LDADD) $(LIBS)
@@ -395,9 +528,31 @@ cuffdiff$(EXEEXT): $(cuffdiff_OBJECTS) $(cuffdiff_DEPENDENCIES)
 cufflinks$(EXEEXT): $(cufflinks_OBJECTS) $(cufflinks_DEPENDENCIES) 
 	@rm -f cufflinks$(EXEEXT)
 	$(CXXLINK) $(cufflinks_LDFLAGS) $(cufflinks_OBJECTS) $(cufflinks_LDADD) $(LIBS)
+gffread$(EXEEXT): $(gffread_OBJECTS) $(gffread_DEPENDENCIES) 
+	@rm -f gffread$(EXEEXT)
+	$(CXXLINK) $(gffread_LDFLAGS) $(gffread_OBJECTS) $(gffread_LDADD) $(LIBS)
 gtf_to_sam$(EXEEXT): $(gtf_to_sam_OBJECTS) $(gtf_to_sam_DEPENDENCIES) 
 	@rm -f gtf_to_sam$(EXEEXT)
 	$(CXXLINK) $(gtf_to_sam_LDFLAGS) $(gtf_to_sam_OBJECTS) $(gtf_to_sam_LDADD) $(LIBS)
+install-binSCRIPTS: $(bin_SCRIPTS)
+	@$(NORMAL_INSTALL)
+	test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
+	@list='$(bin_SCRIPTS)'; for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  if test -f $$d$$p; then \
+	    f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
+	    echo " $(binSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(bindir)/$$f'"; \
+	    $(binSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(bindir)/$$f"; \
+	  else :; fi; \
+	done
+
+uninstall-binSCRIPTS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(bin_SCRIPTS)'; for p in $$list; do \
+	  f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
+	  echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
+	  rm -f "$(DESTDIR)$(bindir)/$$f"; \
+	done
 
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
@@ -411,29 +566,798 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/GFastaIndex.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/GStr.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/abundances.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/adap.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/ar_funs.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/arith.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/assemble.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/band.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/biascorrection.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/bundles.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/c_args.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/c_plot.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/clustering.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/cmd.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/codons.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/common.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/compress_gtf.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/cuffcompare.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/cuffdiff.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/cufflinks.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/dens_haz.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/dens_int.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/dens_odi.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/density.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/differential.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/dist.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/ev_atree.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/ev_interp.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/ev_kdtre.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/ev_main.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/ev_trian.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/family.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/filters.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/fitted.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/frend.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/gdna.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/genes.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/gff.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/gffread.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/graph_optimize.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/gtf_to_sam.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/gtf_tracking.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/help.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/hits.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/jensen_shannon.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/lf_dercor.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/lf_fitfun.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/lf_robust.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/lf_vari.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/lfd.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/lfstr.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/linalg.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/locfit.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/m_chol.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/m_eigen.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/m_jacob.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/m_max.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/makecmd.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/matching_merge.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/math.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/minmax.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/multireads.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/nbhd.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/pcomp.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/pout.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/preplot.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/random.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/readfile.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/replicates.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/scaffold_graph.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/scaffolds.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/scb.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/scb_cons.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/simul.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/solve.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/startlf.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/strings.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/tokenize.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/vari.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/wdiag.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/weight.Po at am__quote@
+
+.c.o:
+ at am__fastdepCC_TRUE@	if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(COMPILE) -c $<
+
+.c.obj:
+ at am__fastdepCC_TRUE@	if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+adap.o: locfit/adap.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT adap.o -MD -MP -MF "$(DEPDIR)/adap.Tpo" -c -o adap.o `test -f 'locfit/adap.c' || echo '$(srcdir)/'`locfit/adap.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/adap.Tpo" "$(DEPDIR)/adap.Po"; else rm -f "$(DEPDIR)/adap.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/adap.c' object='adap.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o adap.o `test -f 'locfit/adap.c' || echo '$(srcdir)/'`locfit/adap.c
+
+adap.obj: locfit/adap.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT adap.obj -MD -MP -MF "$(DEPDIR)/adap.Tpo" -c -o adap.obj `if test -f 'locfit/adap.c'; then $(CYGPATH_W) 'locfit/adap.c'; else $(CYGPATH_W) '$(srcdir)/locfit/adap.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/adap.Tpo" "$(DEPDIR)/adap.Po"; else rm -f "$(DEPDIR)/adap.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/adap.c' object='adap.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o adap.obj `if test -f 'locfit/adap.c'; then $(CYGPATH_W) 'locfit/adap.c'; else $(CYGPATH_W) '$(srcdir)/locfit/adap.c'; fi`
+
+ar_funs.o: locfit/ar_funs.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ar_funs.o -MD -MP -MF "$(DEPDIR)/ar_funs.Tpo" -c -o ar_funs.o `test -f 'locfit/ar_funs.c' || echo '$(srcdir)/'`locfit/ar_funs.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/ar_funs.Tpo" "$(DEPDIR)/ar_funs.Po"; else rm -f "$(DEPDIR)/ar_funs.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/ar_funs.c' object='ar_funs.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ar_funs.o `test -f 'locfit/ar_funs.c' || echo '$(srcdir)/'`locfit/ar_funs.c
+
+ar_funs.obj: locfit/ar_funs.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ar_funs.obj -MD -MP -MF "$(DEPDIR)/ar_funs.Tpo" -c -o ar_funs.obj `if test -f 'locfit/ar_funs.c'; then $(CYGPATH_W) 'locfit/ar_funs.c'; else $(CYGPATH_W) '$(srcdir)/locfit/ar_funs.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/ar_funs.Tpo" "$(DEPDIR)/ar_funs.Po"; else rm -f "$(DEPDIR)/ar_funs.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/ar_funs.c' object='ar_funs.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ar_funs.obj `if test -f 'locfit/ar_funs.c'; then $(CYGPATH_W) 'locfit/ar_funs.c'; else $(CYGPATH_W) '$(srcdir)/locfit/ar_funs.c'; fi`
+
+arith.o: locfit/arith.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT arith.o -MD -MP -MF "$(DEPDIR)/arith.Tpo" -c -o arith.o `test -f 'locfit/arith.c' || echo '$(srcdir)/'`locfit/arith.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/arith.Tpo" "$(DEPDIR)/arith.Po"; else rm -f "$(DEPDIR)/arith.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/arith.c' object='arith.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o arith.o `test -f 'locfit/arith.c' || echo '$(srcdir)/'`locfit/arith.c
+
+arith.obj: locfit/arith.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT arith.obj -MD -MP -MF "$(DEPDIR)/arith.Tpo" -c -o arith.obj `if test -f 'locfit/arith.c'; then $(CYGPATH_W) 'locfit/arith.c'; else $(CYGPATH_W) '$(srcdir)/locfit/arith.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/arith.Tpo" "$(DEPDIR)/arith.Po"; else rm -f "$(DEPDIR)/arith.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/arith.c' object='arith.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o arith.obj `if test -f 'locfit/arith.c'; then $(CYGPATH_W) 'locfit/arith.c'; else $(CYGPATH_W) '$(srcdir)/locfit/arith.c'; fi`
+
+band.o: locfit/band.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT band.o -MD -MP -MF "$(DEPDIR)/band.Tpo" -c -o band.o `test -f 'locfit/band.c' || echo '$(srcdir)/'`locfit/band.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/band.Tpo" "$(DEPDIR)/band.Po"; else rm -f "$(DEPDIR)/band.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/band.c' object='band.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o band.o `test -f 'locfit/band.c' || echo '$(srcdir)/'`locfit/band.c
+
+band.obj: locfit/band.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT band.obj -MD -MP -MF "$(DEPDIR)/band.Tpo" -c -o band.obj `if test -f 'locfit/band.c'; then $(CYGPATH_W) 'locfit/band.c'; else $(CYGPATH_W) '$(srcdir)/locfit/band.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/band.Tpo" "$(DEPDIR)/band.Po"; else rm -f "$(DEPDIR)/band.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/band.c' object='band.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o band.obj `if test -f 'locfit/band.c'; then $(CYGPATH_W) 'locfit/band.c'; else $(CYGPATH_W) '$(srcdir)/locfit/band.c'; fi`
+
+c_args.o: locfit/c_args.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT c_args.o -MD -MP -MF "$(DEPDIR)/c_args.Tpo" -c -o c_args.o `test -f 'locfit/c_args.c' || echo '$(srcdir)/'`locfit/c_args.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/c_args.Tpo" "$(DEPDIR)/c_args.Po"; else rm -f "$(DEPDIR)/c_args.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/c_args.c' object='c_args.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o c_args.o `test -f 'locfit/c_args.c' || echo '$(srcdir)/'`locfit/c_args.c
+
+c_args.obj: locfit/c_args.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT c_args.obj -MD -MP -MF "$(DEPDIR)/c_args.Tpo" -c -o c_args.obj `if test -f 'locfit/c_args.c'; then $(CYGPATH_W) 'locfit/c_args.c'; else $(CYGPATH_W) '$(srcdir)/locfit/c_args.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/c_args.Tpo" "$(DEPDIR)/c_args.Po"; else rm -f "$(DEPDIR)/c_args.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/c_args.c' object='c_args.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o c_args.obj `if test -f 'locfit/c_args.c'; then $(CYGPATH_W) 'locfit/c_args.c'; else $(CYGPATH_W) '$(srcdir)/locfit/c_args.c'; fi`
+
+c_plot.o: locfit/c_plot.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT c_plot.o -MD -MP -MF "$(DEPDIR)/c_plot.Tpo" -c -o c_plot.o `test -f 'locfit/c_plot.c' || echo '$(srcdir)/'`locfit/c_plot.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/c_plot.Tpo" "$(DEPDIR)/c_plot.Po"; else rm -f "$(DEPDIR)/c_plot.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/c_plot.c' object='c_plot.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o c_plot.o `test -f 'locfit/c_plot.c' || echo '$(srcdir)/'`locfit/c_plot.c
+
+c_plot.obj: locfit/c_plot.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT c_plot.obj -MD -MP -MF "$(DEPDIR)/c_plot.Tpo" -c -o c_plot.obj `if test -f 'locfit/c_plot.c'; then $(CYGPATH_W) 'locfit/c_plot.c'; else $(CYGPATH_W) '$(srcdir)/locfit/c_plot.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/c_plot.Tpo" "$(DEPDIR)/c_plot.Po"; else rm -f "$(DEPDIR)/c_plot.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/c_plot.c' object='c_plot.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o c_plot.obj `if test -f 'locfit/c_plot.c'; then $(CYGPATH_W) 'locfit/c_plot.c'; else $(CYGPATH_W) '$(srcdir)/locfit/c_plot.c'; fi`
+
+cmd.o: locfit/cmd.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cmd.o -MD -MP -MF "$(DEPDIR)/cmd.Tpo" -c -o cmd.o `test -f 'locfit/cmd.c' || echo '$(srcdir)/'`locfit/cmd.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/cmd.Tpo" "$(DEPDIR)/cmd.Po"; else rm -f "$(DEPDIR)/cmd.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/cmd.c' object='cmd.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cmd.o `test -f 'locfit/cmd.c' || echo '$(srcdir)/'`locfit/cmd.c
+
+cmd.obj: locfit/cmd.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT cmd.obj -MD -MP -MF "$(DEPDIR)/cmd.Tpo" -c -o cmd.obj `if test -f 'locfit/cmd.c'; then $(CYGPATH_W) 'locfit/cmd.c'; else $(CYGPATH_W) '$(srcdir)/locfit/cmd.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/cmd.Tpo" "$(DEPDIR)/cmd.Po"; else rm -f "$(DEPDIR)/cmd.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/cmd.c' object='cmd.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cmd.obj `if test -f 'locfit/cmd.c'; then $(CYGPATH_W) 'locfit/cmd.c'; else $(CYGPATH_W) '$(srcdir)/locfit/cmd.c'; fi`
+
+dens_haz.o: locfit/dens_haz.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dens_haz.o -MD -MP -MF "$(DEPDIR)/dens_haz.Tpo" -c -o dens_haz.o `test -f 'locfit/dens_haz.c' || echo '$(srcdir)/'`locfit/dens_haz.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/dens_haz.Tpo" "$(DEPDIR)/dens_haz.Po"; else rm -f "$(DEPDIR)/dens_haz.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/dens_haz.c' object='dens_haz.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dens_haz.o `test -f 'locfit/dens_haz.c' || echo '$(srcdir)/'`locfit/dens_haz.c
+
+dens_haz.obj: locfit/dens_haz.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dens_haz.obj -MD -MP -MF "$(DEPDIR)/dens_haz.Tpo" -c -o dens_haz.obj `if test -f 'locfit/dens_haz.c'; then $(CYGPATH_W) 'locfit/dens_haz.c'; else $(CYGPATH_W) '$(srcdir)/locfit/dens_haz.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/dens_haz.Tpo" "$(DEPDIR)/dens_haz.Po"; else rm -f "$(DEPDIR)/dens_haz.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/dens_haz.c' object='dens_haz.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dens_haz.obj `if test -f 'locfit/dens_haz.c'; then $(CYGPATH_W) 'locfit/dens_haz.c'; else $(CYGPATH_W) '$(srcdir)/locfit/dens_haz.c'; fi`
+
+dens_int.o: locfit/dens_int.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dens_int.o -MD -MP -MF "$(DEPDIR)/dens_int.Tpo" -c -o dens_int.o `test -f 'locfit/dens_int.c' || echo '$(srcdir)/'`locfit/dens_int.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/dens_int.Tpo" "$(DEPDIR)/dens_int.Po"; else rm -f "$(DEPDIR)/dens_int.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/dens_int.c' object='dens_int.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dens_int.o `test -f 'locfit/dens_int.c' || echo '$(srcdir)/'`locfit/dens_int.c
+
+dens_int.obj: locfit/dens_int.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dens_int.obj -MD -MP -MF "$(DEPDIR)/dens_int.Tpo" -c -o dens_int.obj `if test -f 'locfit/dens_int.c'; then $(CYGPATH_W) 'locfit/dens_int.c'; else $(CYGPATH_W) '$(srcdir)/locfit/dens_int.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/dens_int.Tpo" "$(DEPDIR)/dens_int.Po"; else rm -f "$(DEPDIR)/dens_int.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/dens_int.c' object='dens_int.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dens_int.obj `if test -f 'locfit/dens_int.c'; then $(CYGPATH_W) 'locfit/dens_int.c'; else $(CYGPATH_W) '$(srcdir)/locfit/dens_int.c'; fi`
+
+dens_odi.o: locfit/dens_odi.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dens_odi.o -MD -MP -MF "$(DEPDIR)/dens_odi.Tpo" -c -o dens_odi.o `test -f 'locfit/dens_odi.c' || echo '$(srcdir)/'`locfit/dens_odi.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/dens_odi.Tpo" "$(DEPDIR)/dens_odi.Po"; else rm -f "$(DEPDIR)/dens_odi.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/dens_odi.c' object='dens_odi.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dens_odi.o `test -f 'locfit/dens_odi.c' || echo '$(srcdir)/'`locfit/dens_odi.c
+
+dens_odi.obj: locfit/dens_odi.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dens_odi.obj -MD -MP -MF "$(DEPDIR)/dens_odi.Tpo" -c -o dens_odi.obj `if test -f 'locfit/dens_odi.c'; then $(CYGPATH_W) 'locfit/dens_odi.c'; else $(CYGPATH_W) '$(srcdir)/locfit/dens_odi.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/dens_odi.Tpo" "$(DEPDIR)/dens_odi.Po"; else rm -f "$(DEPDIR)/dens_odi.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/dens_odi.c' object='dens_odi.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dens_odi.obj `if test -f 'locfit/dens_odi.c'; then $(CYGPATH_W) 'locfit/dens_odi.c'; else $(CYGPATH_W) '$(srcdir)/locfit/dens_odi.c'; fi`
+
+density.o: locfit/density.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT density.o -MD -MP -MF "$(DEPDIR)/density.Tpo" -c -o density.o `test -f 'locfit/density.c' || echo '$(srcdir)/'`locfit/density.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/density.Tpo" "$(DEPDIR)/density.Po"; else rm -f "$(DEPDIR)/density.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/density.c' object='density.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o density.o `test -f 'locfit/density.c' || echo '$(srcdir)/'`locfit/density.c
+
+density.obj: locfit/density.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT density.obj -MD -MP -MF "$(DEPDIR)/density.Tpo" -c -o density.obj `if test -f 'locfit/density.c'; then $(CYGPATH_W) 'locfit/density.c'; else $(CYGPATH_W) '$(srcdir)/locfit/density.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/density.Tpo" "$(DEPDIR)/density.Po"; else rm -f "$(DEPDIR)/density.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/density.c' object='density.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o density.obj `if test -f 'locfit/density.c'; then $(CYGPATH_W) 'locfit/density.c'; else $(CYGPATH_W) '$(srcdir)/locfit/density.c'; fi`
+
+dist.o: locfit/dist.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dist.o -MD -MP -MF "$(DEPDIR)/dist.Tpo" -c -o dist.o `test -f 'locfit/dist.c' || echo '$(srcdir)/'`locfit/dist.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/dist.Tpo" "$(DEPDIR)/dist.Po"; else rm -f "$(DEPDIR)/dist.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/dist.c' object='dist.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dist.o `test -f 'locfit/dist.c' || echo '$(srcdir)/'`locfit/dist.c
+
+dist.obj: locfit/dist.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT dist.obj -MD -MP -MF "$(DEPDIR)/dist.Tpo" -c -o dist.obj `if test -f 'locfit/dist.c'; then $(CYGPATH_W) 'locfit/dist.c'; else $(CYGPATH_W) '$(srcdir)/locfit/dist.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/dist.Tpo" "$(DEPDIR)/dist.Po"; else rm -f "$(DEPDIR)/dist.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/dist.c' object='dist.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o dist.obj `if test -f 'locfit/dist.c'; then $(CYGPATH_W) 'locfit/dist.c'; else $(CYGPATH_W) '$(srcdir)/locfit/dist.c'; fi`
+
+ev_atree.o: locfit/ev_atree.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ev_atree.o -MD -MP -MF "$(DEPDIR)/ev_atree.Tpo" -c -o ev_atree.o `test -f 'locfit/ev_atree.c' || echo '$(srcdir)/'`locfit/ev_atree.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/ev_atree.Tpo" "$(DEPDIR)/ev_atree.Po"; else rm -f "$(DEPDIR)/ev_atree.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/ev_atree.c' object='ev_atree.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ev_atree.o `test -f 'locfit/ev_atree.c' || echo '$(srcdir)/'`locfit/ev_atree.c
+
+ev_atree.obj: locfit/ev_atree.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ev_atree.obj -MD -MP -MF "$(DEPDIR)/ev_atree.Tpo" -c -o ev_atree.obj `if test -f 'locfit/ev_atree.c'; then $(CYGPATH_W) 'locfit/ev_atree.c'; else $(CYGPATH_W) '$(srcdir)/locfit/ev_atree.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/ev_atree.Tpo" "$(DEPDIR)/ev_atree.Po"; else rm -f "$(DEPDIR)/ev_atree.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/ev_atree.c' object='ev_atree.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ev_atree.obj `if test -f 'locfit/ev_atree.c'; then $(CYGPATH_W) 'locfit/ev_atree.c'; else $(CYGPATH_W) '$(srcdir)/locfit/ev_atree.c'; fi`
+
+ev_interp.o: locfit/ev_interp.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ev_interp.o -MD -MP -MF "$(DEPDIR)/ev_interp.Tpo" -c -o ev_interp.o `test -f 'locfit/ev_interp.c' || echo '$(srcdir)/'`locfit/ev_interp.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/ev_interp.Tpo" "$(DEPDIR)/ev_interp.Po"; else rm -f "$(DEPDIR)/ev_interp.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/ev_interp.c' object='ev_interp.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ev_interp.o `test -f 'locfit/ev_interp.c' || echo '$(srcdir)/'`locfit/ev_interp.c
+
+ev_interp.obj: locfit/ev_interp.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ev_interp.obj -MD -MP -MF "$(DEPDIR)/ev_interp.Tpo" -c -o ev_interp.obj `if test -f 'locfit/ev_interp.c'; then $(CYGPATH_W) 'locfit/ev_interp.c'; else $(CYGPATH_W) '$(srcdir)/locfit/ev_interp.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/ev_interp.Tpo" "$(DEPDIR)/ev_interp.Po"; else rm -f "$(DEPDIR)/ev_interp.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/ev_interp.c' object='ev_interp.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ev_interp.obj `if test -f 'locfit/ev_interp.c'; then $(CYGPATH_W) 'locfit/ev_interp.c'; else $(CYGPATH_W) '$(srcdir)/locfit/ev_interp.c'; fi`
+
+ev_kdtre.o: locfit/ev_kdtre.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ev_kdtre.o -MD -MP -MF "$(DEPDIR)/ev_kdtre.Tpo" -c -o ev_kdtre.o `test -f 'locfit/ev_kdtre.c' || echo '$(srcdir)/'`locfit/ev_kdtre.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/ev_kdtre.Tpo" "$(DEPDIR)/ev_kdtre.Po"; else rm -f "$(DEPDIR)/ev_kdtre.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/ev_kdtre.c' object='ev_kdtre.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ev_kdtre.o `test -f 'locfit/ev_kdtre.c' || echo '$(srcdir)/'`locfit/ev_kdtre.c
+
+ev_kdtre.obj: locfit/ev_kdtre.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ev_kdtre.obj -MD -MP -MF "$(DEPDIR)/ev_kdtre.Tpo" -c -o ev_kdtre.obj `if test -f 'locfit/ev_kdtre.c'; then $(CYGPATH_W) 'locfit/ev_kdtre.c'; else $(CYGPATH_W) '$(srcdir)/locfit/ev_kdtre.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/ev_kdtre.Tpo" "$(DEPDIR)/ev_kdtre.Po"; else rm -f "$(DEPDIR)/ev_kdtre.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/ev_kdtre.c' object='ev_kdtre.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ev_kdtre.obj `if test -f 'locfit/ev_kdtre.c'; then $(CYGPATH_W) 'locfit/ev_kdtre.c'; else $(CYGPATH_W) '$(srcdir)/locfit/ev_kdtre.c'; fi`
+
+ev_main.o: locfit/ev_main.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ev_main.o -MD -MP -MF "$(DEPDIR)/ev_main.Tpo" -c -o ev_main.o `test -f 'locfit/ev_main.c' || echo '$(srcdir)/'`locfit/ev_main.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/ev_main.Tpo" "$(DEPDIR)/ev_main.Po"; else rm -f "$(DEPDIR)/ev_main.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/ev_main.c' object='ev_main.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ev_main.o `test -f 'locfit/ev_main.c' || echo '$(srcdir)/'`locfit/ev_main.c
+
+ev_main.obj: locfit/ev_main.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ev_main.obj -MD -MP -MF "$(DEPDIR)/ev_main.Tpo" -c -o ev_main.obj `if test -f 'locfit/ev_main.c'; then $(CYGPATH_W) 'locfit/ev_main.c'; else $(CYGPATH_W) '$(srcdir)/locfit/ev_main.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/ev_main.Tpo" "$(DEPDIR)/ev_main.Po"; else rm -f "$(DEPDIR)/ev_main.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/ev_main.c' object='ev_main.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ev_main.obj `if test -f 'locfit/ev_main.c'; then $(CYGPATH_W) 'locfit/ev_main.c'; else $(CYGPATH_W) '$(srcdir)/locfit/ev_main.c'; fi`
+
+ev_trian.o: locfit/ev_trian.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ev_trian.o -MD -MP -MF "$(DEPDIR)/ev_trian.Tpo" -c -o ev_trian.o `test -f 'locfit/ev_trian.c' || echo '$(srcdir)/'`locfit/ev_trian.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/ev_trian.Tpo" "$(DEPDIR)/ev_trian.Po"; else rm -f "$(DEPDIR)/ev_trian.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/ev_trian.c' object='ev_trian.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ev_trian.o `test -f 'locfit/ev_trian.c' || echo '$(srcdir)/'`locfit/ev_trian.c
+
+ev_trian.obj: locfit/ev_trian.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ev_trian.obj -MD -MP -MF "$(DEPDIR)/ev_trian.Tpo" -c -o ev_trian.obj `if test -f 'locfit/ev_trian.c'; then $(CYGPATH_W) 'locfit/ev_trian.c'; else $(CYGPATH_W) '$(srcdir)/locfit/ev_trian.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/ev_trian.Tpo" "$(DEPDIR)/ev_trian.Po"; else rm -f "$(DEPDIR)/ev_trian.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/ev_trian.c' object='ev_trian.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ev_trian.obj `if test -f 'locfit/ev_trian.c'; then $(CYGPATH_W) 'locfit/ev_trian.c'; else $(CYGPATH_W) '$(srcdir)/locfit/ev_trian.c'; fi`
+
+family.o: locfit/family.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT family.o -MD -MP -MF "$(DEPDIR)/family.Tpo" -c -o family.o `test -f 'locfit/family.c' || echo '$(srcdir)/'`locfit/family.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/family.Tpo" "$(DEPDIR)/family.Po"; else rm -f "$(DEPDIR)/family.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/family.c' object='family.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o family.o `test -f 'locfit/family.c' || echo '$(srcdir)/'`locfit/family.c
+
+family.obj: locfit/family.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT family.obj -MD -MP -MF "$(DEPDIR)/family.Tpo" -c -o family.obj `if test -f 'locfit/family.c'; then $(CYGPATH_W) 'locfit/family.c'; else $(CYGPATH_W) '$(srcdir)/locfit/family.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/family.Tpo" "$(DEPDIR)/family.Po"; else rm -f "$(DEPDIR)/family.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/family.c' object='family.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o family.obj `if test -f 'locfit/family.c'; then $(CYGPATH_W) 'locfit/family.c'; else $(CYGPATH_W) '$(srcdir)/locfit/family.c'; fi`
+
+fitted.o: locfit/fitted.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fitted.o -MD -MP -MF "$(DEPDIR)/fitted.Tpo" -c -o fitted.o `test -f 'locfit/fitted.c' || echo '$(srcdir)/'`locfit/fitted.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/fitted.Tpo" "$(DEPDIR)/fitted.Po"; else rm -f "$(DEPDIR)/fitted.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/fitted.c' object='fitted.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fitted.o `test -f 'locfit/fitted.c' || echo '$(srcdir)/'`locfit/fitted.c
+
+fitted.obj: locfit/fitted.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fitted.obj -MD -MP -MF "$(DEPDIR)/fitted.Tpo" -c -o fitted.obj `if test -f 'locfit/fitted.c'; then $(CYGPATH_W) 'locfit/fitted.c'; else $(CYGPATH_W) '$(srcdir)/locfit/fitted.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/fitted.Tpo" "$(DEPDIR)/fitted.Po"; else rm -f "$(DEPDIR)/fitted.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/fitted.c' object='fitted.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fitted.obj `if test -f 'locfit/fitted.c'; then $(CYGPATH_W) 'locfit/fitted.c'; else $(CYGPATH_W) '$(srcdir)/locfit/fitted.c'; fi`
+
+frend.o: locfit/frend.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT frend.o -MD -MP -MF "$(DEPDIR)/frend.Tpo" -c -o frend.o `test -f 'locfit/frend.c' || echo '$(srcdir)/'`locfit/frend.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/frend.Tpo" "$(DEPDIR)/frend.Po"; else rm -f "$(DEPDIR)/frend.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/frend.c' object='frend.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o frend.o `test -f 'locfit/frend.c' || echo '$(srcdir)/'`locfit/frend.c
+
+frend.obj: locfit/frend.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT frend.obj -MD -MP -MF "$(DEPDIR)/frend.Tpo" -c -o frend.obj `if test -f 'locfit/frend.c'; then $(CYGPATH_W) 'locfit/frend.c'; else $(CYGPATH_W) '$(srcdir)/locfit/frend.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/frend.Tpo" "$(DEPDIR)/frend.Po"; else rm -f "$(DEPDIR)/frend.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/frend.c' object='frend.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o frend.obj `if test -f 'locfit/frend.c'; then $(CYGPATH_W) 'locfit/frend.c'; else $(CYGPATH_W) '$(srcdir)/locfit/frend.c'; fi`
+
+help.o: locfit/help.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT help.o -MD -MP -MF "$(DEPDIR)/help.Tpo" -c -o help.o `test -f 'locfit/help.c' || echo '$(srcdir)/'`locfit/help.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/help.Tpo" "$(DEPDIR)/help.Po"; else rm -f "$(DEPDIR)/help.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/help.c' object='help.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o help.o `test -f 'locfit/help.c' || echo '$(srcdir)/'`locfit/help.c
+
+help.obj: locfit/help.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT help.obj -MD -MP -MF "$(DEPDIR)/help.Tpo" -c -o help.obj `if test -f 'locfit/help.c'; then $(CYGPATH_W) 'locfit/help.c'; else $(CYGPATH_W) '$(srcdir)/locfit/help.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/help.Tpo" "$(DEPDIR)/help.Po"; else rm -f "$(DEPDIR)/help.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/help.c' object='help.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o help.obj `if test -f 'locfit/help.c'; then $(CYGPATH_W) 'locfit/help.c'; else $(CYGPATH_W) '$(srcdir)/locfit/help.c'; fi`
+
+lf_dercor.o: locfit/lf_dercor.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lf_dercor.o -MD -MP -MF "$(DEPDIR)/lf_dercor.Tpo" -c -o lf_dercor.o `test -f 'locfit/lf_dercor.c' || echo '$(srcdir)/'`locfit/lf_dercor.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/lf_dercor.Tpo" "$(DEPDIR)/lf_dercor.Po"; else rm -f "$(DEPDIR)/lf_dercor.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/lf_dercor.c' object='lf_dercor.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lf_dercor.o `test -f 'locfit/lf_dercor.c' || echo '$(srcdir)/'`locfit/lf_dercor.c
+
+lf_dercor.obj: locfit/lf_dercor.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lf_dercor.obj -MD -MP -MF "$(DEPDIR)/lf_dercor.Tpo" -c -o lf_dercor.obj `if test -f 'locfit/lf_dercor.c'; then $(CYGPATH_W) 'locfit/lf_dercor.c'; else $(CYGPATH_W) '$(srcdir)/locfit/lf_dercor.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/lf_dercor.Tpo" "$(DEPDIR)/lf_dercor.Po"; else rm -f "$(DEPDIR)/lf_dercor.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/lf_dercor.c' object='lf_dercor.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lf_dercor.obj `if test -f 'locfit/lf_dercor.c'; then $(CYGPATH_W) 'locfit/lf_dercor.c'; else $(CYGPATH_W) '$(srcdir)/locfit/lf_dercor.c'; fi`
+
+lf_fitfun.o: locfit/lf_fitfun.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lf_fitfun.o -MD -MP -MF "$(DEPDIR)/lf_fitfun.Tpo" -c -o lf_fitfun.o `test -f 'locfit/lf_fitfun.c' || echo '$(srcdir)/'`locfit/lf_fitfun.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/lf_fitfun.Tpo" "$(DEPDIR)/lf_fitfun.Po"; else rm -f "$(DEPDIR)/lf_fitfun.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/lf_fitfun.c' object='lf_fitfun.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lf_fitfun.o `test -f 'locfit/lf_fitfun.c' || echo '$(srcdir)/'`locfit/lf_fitfun.c
+
+lf_fitfun.obj: locfit/lf_fitfun.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lf_fitfun.obj -MD -MP -MF "$(DEPDIR)/lf_fitfun.Tpo" -c -o lf_fitfun.obj `if test -f 'locfit/lf_fitfun.c'; then $(CYGPATH_W) 'locfit/lf_fitfun.c'; else $(CYGPATH_W) '$(srcdir)/locfit/lf_fitfun.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/lf_fitfun.Tpo" "$(DEPDIR)/lf_fitfun.Po"; else rm -f "$(DEPDIR)/lf_fitfun.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/lf_fitfun.c' object='lf_fitfun.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lf_fitfun.obj `if test -f 'locfit/lf_fitfun.c'; then $(CYGPATH_W) 'locfit/lf_fitfun.c'; else $(CYGPATH_W) '$(srcdir)/locfit/lf_fitfun.c'; fi`
+
+lf_robust.o: locfit/lf_robust.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lf_robust.o -MD -MP -MF "$(DEPDIR)/lf_robust.Tpo" -c -o lf_robust.o `test -f 'locfit/lf_robust.c' || echo '$(srcdir)/'`locfit/lf_robust.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/lf_robust.Tpo" "$(DEPDIR)/lf_robust.Po"; else rm -f "$(DEPDIR)/lf_robust.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/lf_robust.c' object='lf_robust.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lf_robust.o `test -f 'locfit/lf_robust.c' || echo '$(srcdir)/'`locfit/lf_robust.c
+
+lf_robust.obj: locfit/lf_robust.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lf_robust.obj -MD -MP -MF "$(DEPDIR)/lf_robust.Tpo" -c -o lf_robust.obj `if test -f 'locfit/lf_robust.c'; then $(CYGPATH_W) 'locfit/lf_robust.c'; else $(CYGPATH_W) '$(srcdir)/locfit/lf_robust.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/lf_robust.Tpo" "$(DEPDIR)/lf_robust.Po"; else rm -f "$(DEPDIR)/lf_robust.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/lf_robust.c' object='lf_robust.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lf_robust.obj `if test -f 'locfit/lf_robust.c'; then $(CYGPATH_W) 'locfit/lf_robust.c'; else $(CYGPATH_W) '$(srcdir)/locfit/lf_robust.c'; fi`
+
+lf_vari.o: locfit/lf_vari.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lf_vari.o -MD -MP -MF "$(DEPDIR)/lf_vari.Tpo" -c -o lf_vari.o `test -f 'locfit/lf_vari.c' || echo '$(srcdir)/'`locfit/lf_vari.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/lf_vari.Tpo" "$(DEPDIR)/lf_vari.Po"; else rm -f "$(DEPDIR)/lf_vari.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/lf_vari.c' object='lf_vari.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lf_vari.o `test -f 'locfit/lf_vari.c' || echo '$(srcdir)/'`locfit/lf_vari.c
+
+lf_vari.obj: locfit/lf_vari.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lf_vari.obj -MD -MP -MF "$(DEPDIR)/lf_vari.Tpo" -c -o lf_vari.obj `if test -f 'locfit/lf_vari.c'; then $(CYGPATH_W) 'locfit/lf_vari.c'; else $(CYGPATH_W) '$(srcdir)/locfit/lf_vari.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/lf_vari.Tpo" "$(DEPDIR)/lf_vari.Po"; else rm -f "$(DEPDIR)/lf_vari.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/lf_vari.c' object='lf_vari.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lf_vari.obj `if test -f 'locfit/lf_vari.c'; then $(CYGPATH_W) 'locfit/lf_vari.c'; else $(CYGPATH_W) '$(srcdir)/locfit/lf_vari.c'; fi`
+
+lfd.o: locfit/lfd.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lfd.o -MD -MP -MF "$(DEPDIR)/lfd.Tpo" -c -o lfd.o `test -f 'locfit/lfd.c' || echo '$(srcdir)/'`locfit/lfd.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/lfd.Tpo" "$(DEPDIR)/lfd.Po"; else rm -f "$(DEPDIR)/lfd.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/lfd.c' object='lfd.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lfd.o `test -f 'locfit/lfd.c' || echo '$(srcdir)/'`locfit/lfd.c
+
+lfd.obj: locfit/lfd.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lfd.obj -MD -MP -MF "$(DEPDIR)/lfd.Tpo" -c -o lfd.obj `if test -f 'locfit/lfd.c'; then $(CYGPATH_W) 'locfit/lfd.c'; else $(CYGPATH_W) '$(srcdir)/locfit/lfd.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/lfd.Tpo" "$(DEPDIR)/lfd.Po"; else rm -f "$(DEPDIR)/lfd.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/lfd.c' object='lfd.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lfd.obj `if test -f 'locfit/lfd.c'; then $(CYGPATH_W) 'locfit/lfd.c'; else $(CYGPATH_W) '$(srcdir)/locfit/lfd.c'; fi`
+
+lfstr.o: locfit/lfstr.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lfstr.o -MD -MP -MF "$(DEPDIR)/lfstr.Tpo" -c -o lfstr.o `test -f 'locfit/lfstr.c' || echo '$(srcdir)/'`locfit/lfstr.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/lfstr.Tpo" "$(DEPDIR)/lfstr.Po"; else rm -f "$(DEPDIR)/lfstr.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/lfstr.c' object='lfstr.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lfstr.o `test -f 'locfit/lfstr.c' || echo '$(srcdir)/'`locfit/lfstr.c
+
+lfstr.obj: locfit/lfstr.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lfstr.obj -MD -MP -MF "$(DEPDIR)/lfstr.Tpo" -c -o lfstr.obj `if test -f 'locfit/lfstr.c'; then $(CYGPATH_W) 'locfit/lfstr.c'; else $(CYGPATH_W) '$(srcdir)/locfit/lfstr.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/lfstr.Tpo" "$(DEPDIR)/lfstr.Po"; else rm -f "$(DEPDIR)/lfstr.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/lfstr.c' object='lfstr.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lfstr.obj `if test -f 'locfit/lfstr.c'; then $(CYGPATH_W) 'locfit/lfstr.c'; else $(CYGPATH_W) '$(srcdir)/locfit/lfstr.c'; fi`
+
+linalg.o: locfit/linalg.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT linalg.o -MD -MP -MF "$(DEPDIR)/linalg.Tpo" -c -o linalg.o `test -f 'locfit/linalg.c' || echo '$(srcdir)/'`locfit/linalg.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/linalg.Tpo" "$(DEPDIR)/linalg.Po"; else rm -f "$(DEPDIR)/linalg.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/linalg.c' object='linalg.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o linalg.o `test -f 'locfit/linalg.c' || echo '$(srcdir)/'`locfit/linalg.c
+
+linalg.obj: locfit/linalg.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT linalg.obj -MD -MP -MF "$(DEPDIR)/linalg.Tpo" -c -o linalg.obj `if test -f 'locfit/linalg.c'; then $(CYGPATH_W) 'locfit/linalg.c'; else $(CYGPATH_W) '$(srcdir)/locfit/linalg.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/linalg.Tpo" "$(DEPDIR)/linalg.Po"; else rm -f "$(DEPDIR)/linalg.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/linalg.c' object='linalg.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o linalg.obj `if test -f 'locfit/linalg.c'; then $(CYGPATH_W) 'locfit/linalg.c'; else $(CYGPATH_W) '$(srcdir)/locfit/linalg.c'; fi`
+
+locfit.o: locfit/locfit.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT locfit.o -MD -MP -MF "$(DEPDIR)/locfit.Tpo" -c -o locfit.o `test -f 'locfit/locfit.c' || echo '$(srcdir)/'`locfit/locfit.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/locfit.Tpo" "$(DEPDIR)/locfit.Po"; else rm -f "$(DEPDIR)/locfit.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/locfit.c' object='locfit.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o locfit.o `test -f 'locfit/locfit.c' || echo '$(srcdir)/'`locfit/locfit.c
+
+locfit.obj: locfit/locfit.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT locfit.obj -MD -MP -MF "$(DEPDIR)/locfit.Tpo" -c -o locfit.obj `if test -f 'locfit/locfit.c'; then $(CYGPATH_W) 'locfit/locfit.c'; else $(CYGPATH_W) '$(srcdir)/locfit/locfit.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/locfit.Tpo" "$(DEPDIR)/locfit.Po"; else rm -f "$(DEPDIR)/locfit.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/locfit.c' object='locfit.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o locfit.obj `if test -f 'locfit/locfit.c'; then $(CYGPATH_W) 'locfit/locfit.c'; else $(CYGPATH_W) '$(srcdir)/locfit/locfit.c'; fi`
+
+m_chol.o: locfit/m_chol.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT m_chol.o -MD -MP -MF "$(DEPDIR)/m_chol.Tpo" -c -o m_chol.o `test -f 'locfit/m_chol.c' || echo '$(srcdir)/'`locfit/m_chol.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/m_chol.Tpo" "$(DEPDIR)/m_chol.Po"; else rm -f "$(DEPDIR)/m_chol.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/m_chol.c' object='m_chol.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o m_chol.o `test -f 'locfit/m_chol.c' || echo '$(srcdir)/'`locfit/m_chol.c
+
+m_chol.obj: locfit/m_chol.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT m_chol.obj -MD -MP -MF "$(DEPDIR)/m_chol.Tpo" -c -o m_chol.obj `if test -f 'locfit/m_chol.c'; then $(CYGPATH_W) 'locfit/m_chol.c'; else $(CYGPATH_W) '$(srcdir)/locfit/m_chol.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/m_chol.Tpo" "$(DEPDIR)/m_chol.Po"; else rm -f "$(DEPDIR)/m_chol.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/m_chol.c' object='m_chol.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o m_chol.obj `if test -f 'locfit/m_chol.c'; then $(CYGPATH_W) 'locfit/m_chol.c'; else $(CYGPATH_W) '$(srcdir)/locfit/m_chol.c'; fi`
+
+m_eigen.o: locfit/m_eigen.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT m_eigen.o -MD -MP -MF "$(DEPDIR)/m_eigen.Tpo" -c -o m_eigen.o `test -f 'locfit/m_eigen.c' || echo '$(srcdir)/'`locfit/m_eigen.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/m_eigen.Tpo" "$(DEPDIR)/m_eigen.Po"; else rm -f "$(DEPDIR)/m_eigen.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/m_eigen.c' object='m_eigen.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o m_eigen.o `test -f 'locfit/m_eigen.c' || echo '$(srcdir)/'`locfit/m_eigen.c
+
+m_eigen.obj: locfit/m_eigen.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT m_eigen.obj -MD -MP -MF "$(DEPDIR)/m_eigen.Tpo" -c -o m_eigen.obj `if test -f 'locfit/m_eigen.c'; then $(CYGPATH_W) 'locfit/m_eigen.c'; else $(CYGPATH_W) '$(srcdir)/locfit/m_eigen.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/m_eigen.Tpo" "$(DEPDIR)/m_eigen.Po"; else rm -f "$(DEPDIR)/m_eigen.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/m_eigen.c' object='m_eigen.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o m_eigen.obj `if test -f 'locfit/m_eigen.c'; then $(CYGPATH_W) 'locfit/m_eigen.c'; else $(CYGPATH_W) '$(srcdir)/locfit/m_eigen.c'; fi`
+
+m_jacob.o: locfit/m_jacob.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT m_jacob.o -MD -MP -MF "$(DEPDIR)/m_jacob.Tpo" -c -o m_jacob.o `test -f 'locfit/m_jacob.c' || echo '$(srcdir)/'`locfit/m_jacob.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/m_jacob.Tpo" "$(DEPDIR)/m_jacob.Po"; else rm -f "$(DEPDIR)/m_jacob.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/m_jacob.c' object='m_jacob.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o m_jacob.o `test -f 'locfit/m_jacob.c' || echo '$(srcdir)/'`locfit/m_jacob.c
+
+m_jacob.obj: locfit/m_jacob.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT m_jacob.obj -MD -MP -MF "$(DEPDIR)/m_jacob.Tpo" -c -o m_jacob.obj `if test -f 'locfit/m_jacob.c'; then $(CYGPATH_W) 'locfit/m_jacob.c'; else $(CYGPATH_W) '$(srcdir)/locfit/m_jacob.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/m_jacob.Tpo" "$(DEPDIR)/m_jacob.Po"; else rm -f "$(DEPDIR)/m_jacob.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/m_jacob.c' object='m_jacob.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o m_jacob.obj `if test -f 'locfit/m_jacob.c'; then $(CYGPATH_W) 'locfit/m_jacob.c'; else $(CYGPATH_W) '$(srcdir)/locfit/m_jacob.c'; fi`
+
+m_max.o: locfit/m_max.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT m_max.o -MD -MP -MF "$(DEPDIR)/m_max.Tpo" -c -o m_max.o `test -f 'locfit/m_max.c' || echo '$(srcdir)/'`locfit/m_max.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/m_max.Tpo" "$(DEPDIR)/m_max.Po"; else rm -f "$(DEPDIR)/m_max.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/m_max.c' object='m_max.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o m_max.o `test -f 'locfit/m_max.c' || echo '$(srcdir)/'`locfit/m_max.c
+
+m_max.obj: locfit/m_max.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT m_max.obj -MD -MP -MF "$(DEPDIR)/m_max.Tpo" -c -o m_max.obj `if test -f 'locfit/m_max.c'; then $(CYGPATH_W) 'locfit/m_max.c'; else $(CYGPATH_W) '$(srcdir)/locfit/m_max.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/m_max.Tpo" "$(DEPDIR)/m_max.Po"; else rm -f "$(DEPDIR)/m_max.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/m_max.c' object='m_max.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o m_max.obj `if test -f 'locfit/m_max.c'; then $(CYGPATH_W) 'locfit/m_max.c'; else $(CYGPATH_W) '$(srcdir)/locfit/m_max.c'; fi`
+
+makecmd.o: locfit/makecmd.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT makecmd.o -MD -MP -MF "$(DEPDIR)/makecmd.Tpo" -c -o makecmd.o `test -f 'locfit/makecmd.c' || echo '$(srcdir)/'`locfit/makecmd.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/makecmd.Tpo" "$(DEPDIR)/makecmd.Po"; else rm -f "$(DEPDIR)/makecmd.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/makecmd.c' object='makecmd.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o makecmd.o `test -f 'locfit/makecmd.c' || echo '$(srcdir)/'`locfit/makecmd.c
+
+makecmd.obj: locfit/makecmd.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT makecmd.obj -MD -MP -MF "$(DEPDIR)/makecmd.Tpo" -c -o makecmd.obj `if test -f 'locfit/makecmd.c'; then $(CYGPATH_W) 'locfit/makecmd.c'; else $(CYGPATH_W) '$(srcdir)/locfit/makecmd.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/makecmd.Tpo" "$(DEPDIR)/makecmd.Po"; else rm -f "$(DEPDIR)/makecmd.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/makecmd.c' object='makecmd.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o makecmd.obj `if test -f 'locfit/makecmd.c'; then $(CYGPATH_W) 'locfit/makecmd.c'; else $(CYGPATH_W) '$(srcdir)/locfit/makecmd.c'; fi`
+
+math.o: locfit/math.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT math.o -MD -MP -MF "$(DEPDIR)/math.Tpo" -c -o math.o `test -f 'locfit/math.c' || echo '$(srcdir)/'`locfit/math.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/math.Tpo" "$(DEPDIR)/math.Po"; else rm -f "$(DEPDIR)/math.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/math.c' object='math.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o math.o `test -f 'locfit/math.c' || echo '$(srcdir)/'`locfit/math.c
+
+math.obj: locfit/math.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT math.obj -MD -MP -MF "$(DEPDIR)/math.Tpo" -c -o math.obj `if test -f 'locfit/math.c'; then $(CYGPATH_W) 'locfit/math.c'; else $(CYGPATH_W) '$(srcdir)/locfit/math.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/math.Tpo" "$(DEPDIR)/math.Po"; else rm -f "$(DEPDIR)/math.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/math.c' object='math.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o math.obj `if test -f 'locfit/math.c'; then $(CYGPATH_W) 'locfit/math.c'; else $(CYGPATH_W) '$(srcdir)/locfit/math.c'; fi`
+
+minmax.o: locfit/minmax.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT minmax.o -MD -MP -MF "$(DEPDIR)/minmax.Tpo" -c -o minmax.o `test -f 'locfit/minmax.c' || echo '$(srcdir)/'`locfit/minmax.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/minmax.Tpo" "$(DEPDIR)/minmax.Po"; else rm -f "$(DEPDIR)/minmax.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/minmax.c' object='minmax.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o minmax.o `test -f 'locfit/minmax.c' || echo '$(srcdir)/'`locfit/minmax.c
+
+minmax.obj: locfit/minmax.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT minmax.obj -MD -MP -MF "$(DEPDIR)/minmax.Tpo" -c -o minmax.obj `if test -f 'locfit/minmax.c'; then $(CYGPATH_W) 'locfit/minmax.c'; else $(CYGPATH_W) '$(srcdir)/locfit/minmax.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/minmax.Tpo" "$(DEPDIR)/minmax.Po"; else rm -f "$(DEPDIR)/minmax.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/minmax.c' object='minmax.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o minmax.obj `if test -f 'locfit/minmax.c'; then $(CYGPATH_W) 'locfit/minmax.c'; else $(CYGPATH_W) '$(srcdir)/locfit/minmax.c'; fi`
+
+nbhd.o: locfit/nbhd.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nbhd.o -MD -MP -MF "$(DEPDIR)/nbhd.Tpo" -c -o nbhd.o `test -f 'locfit/nbhd.c' || echo '$(srcdir)/'`locfit/nbhd.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/nbhd.Tpo" "$(DEPDIR)/nbhd.Po"; else rm -f "$(DEPDIR)/nbhd.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/nbhd.c' object='nbhd.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nbhd.o `test -f 'locfit/nbhd.c' || echo '$(srcdir)/'`locfit/nbhd.c
+
+nbhd.obj: locfit/nbhd.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nbhd.obj -MD -MP -MF "$(DEPDIR)/nbhd.Tpo" -c -o nbhd.obj `if test -f 'locfit/nbhd.c'; then $(CYGPATH_W) 'locfit/nbhd.c'; else $(CYGPATH_W) '$(srcdir)/locfit/nbhd.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/nbhd.Tpo" "$(DEPDIR)/nbhd.Po"; else rm -f "$(DEPDIR)/nbhd.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/nbhd.c' object='nbhd.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nbhd.obj `if test -f 'locfit/nbhd.c'; then $(CYGPATH_W) 'locfit/nbhd.c'; else $(CYGPATH_W) '$(srcdir)/locfit/nbhd.c'; fi`
+
+pcomp.o: locfit/pcomp.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pcomp.o -MD -MP -MF "$(DEPDIR)/pcomp.Tpo" -c -o pcomp.o `test -f 'locfit/pcomp.c' || echo '$(srcdir)/'`locfit/pcomp.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/pcomp.Tpo" "$(DEPDIR)/pcomp.Po"; else rm -f "$(DEPDIR)/pcomp.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/pcomp.c' object='pcomp.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o pcomp.o `test -f 'locfit/pcomp.c' || echo '$(srcdir)/'`locfit/pcomp.c
+
+pcomp.obj: locfit/pcomp.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pcomp.obj -MD -MP -MF "$(DEPDIR)/pcomp.Tpo" -c -o pcomp.obj `if test -f 'locfit/pcomp.c'; then $(CYGPATH_W) 'locfit/pcomp.c'; else $(CYGPATH_W) '$(srcdir)/locfit/pcomp.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/pcomp.Tpo" "$(DEPDIR)/pcomp.Po"; else rm -f "$(DEPDIR)/pcomp.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/pcomp.c' object='pcomp.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o pcomp.obj `if test -f 'locfit/pcomp.c'; then $(CYGPATH_W) 'locfit/pcomp.c'; else $(CYGPATH_W) '$(srcdir)/locfit/pcomp.c'; fi`
+
+pout.o: locfit/pout.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pout.o -MD -MP -MF "$(DEPDIR)/pout.Tpo" -c -o pout.o `test -f 'locfit/pout.c' || echo '$(srcdir)/'`locfit/pout.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/pout.Tpo" "$(DEPDIR)/pout.Po"; else rm -f "$(DEPDIR)/pout.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/pout.c' object='pout.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o pout.o `test -f 'locfit/pout.c' || echo '$(srcdir)/'`locfit/pout.c
+
+pout.obj: locfit/pout.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pout.obj -MD -MP -MF "$(DEPDIR)/pout.Tpo" -c -o pout.obj `if test -f 'locfit/pout.c'; then $(CYGPATH_W) 'locfit/pout.c'; else $(CYGPATH_W) '$(srcdir)/locfit/pout.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/pout.Tpo" "$(DEPDIR)/pout.Po"; else rm -f "$(DEPDIR)/pout.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/pout.c' object='pout.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o pout.obj `if test -f 'locfit/pout.c'; then $(CYGPATH_W) 'locfit/pout.c'; else $(CYGPATH_W) '$(srcdir)/locfit/pout.c'; fi`
+
+preplot.o: locfit/preplot.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT preplot.o -MD -MP -MF "$(DEPDIR)/preplot.Tpo" -c -o preplot.o `test -f 'locfit/preplot.c' || echo '$(srcdir)/'`locfit/preplot.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/preplot.Tpo" "$(DEPDIR)/preplot.Po"; else rm -f "$(DEPDIR)/preplot.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/preplot.c' object='preplot.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o preplot.o `test -f 'locfit/preplot.c' || echo '$(srcdir)/'`locfit/preplot.c
+
+preplot.obj: locfit/preplot.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT preplot.obj -MD -MP -MF "$(DEPDIR)/preplot.Tpo" -c -o preplot.obj `if test -f 'locfit/preplot.c'; then $(CYGPATH_W) 'locfit/preplot.c'; else $(CYGPATH_W) '$(srcdir)/locfit/preplot.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/preplot.Tpo" "$(DEPDIR)/preplot.Po"; else rm -f "$(DEPDIR)/preplot.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/preplot.c' object='preplot.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o preplot.obj `if test -f 'locfit/preplot.c'; then $(CYGPATH_W) 'locfit/preplot.c'; else $(CYGPATH_W) '$(srcdir)/locfit/preplot.c'; fi`
+
+random.o: locfit/random.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT random.o -MD -MP -MF "$(DEPDIR)/random.Tpo" -c -o random.o `test -f 'locfit/random.c' || echo '$(srcdir)/'`locfit/random.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/random.Tpo" "$(DEPDIR)/random.Po"; else rm -f "$(DEPDIR)/random.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/random.c' object='random.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o random.o `test -f 'locfit/random.c' || echo '$(srcdir)/'`locfit/random.c
+
+random.obj: locfit/random.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT random.obj -MD -MP -MF "$(DEPDIR)/random.Tpo" -c -o random.obj `if test -f 'locfit/random.c'; then $(CYGPATH_W) 'locfit/random.c'; else $(CYGPATH_W) '$(srcdir)/locfit/random.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/random.Tpo" "$(DEPDIR)/random.Po"; else rm -f "$(DEPDIR)/random.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/random.c' object='random.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o random.obj `if test -f 'locfit/random.c'; then $(CYGPATH_W) 'locfit/random.c'; else $(CYGPATH_W) '$(srcdir)/locfit/random.c'; fi`
+
+readfile.o: locfit/readfile.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT readfile.o -MD -MP -MF "$(DEPDIR)/readfile.Tpo" -c -o readfile.o `test -f 'locfit/readfile.c' || echo '$(srcdir)/'`locfit/readfile.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/readfile.Tpo" "$(DEPDIR)/readfile.Po"; else rm -f "$(DEPDIR)/readfile.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/readfile.c' object='readfile.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o readfile.o `test -f 'locfit/readfile.c' || echo '$(srcdir)/'`locfit/readfile.c
+
+readfile.obj: locfit/readfile.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT readfile.obj -MD -MP -MF "$(DEPDIR)/readfile.Tpo" -c -o readfile.obj `if test -f 'locfit/readfile.c'; then $(CYGPATH_W) 'locfit/readfile.c'; else $(CYGPATH_W) '$(srcdir)/locfit/readfile.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/readfile.Tpo" "$(DEPDIR)/readfile.Po"; else rm -f "$(DEPDIR)/readfile.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/readfile.c' object='readfile.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o readfile.obj `if test -f 'locfit/readfile.c'; then $(CYGPATH_W) 'locfit/readfile.c'; else $(CYGPATH_W) '$(srcdir)/locfit/readfile.c'; fi`
+
+scb.o: locfit/scb.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT scb.o -MD -MP -MF "$(DEPDIR)/scb.Tpo" -c -o scb.o `test -f 'locfit/scb.c' || echo '$(srcdir)/'`locfit/scb.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/scb.Tpo" "$(DEPDIR)/scb.Po"; else rm -f "$(DEPDIR)/scb.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/scb.c' object='scb.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o scb.o `test -f 'locfit/scb.c' || echo '$(srcdir)/'`locfit/scb.c
+
+scb.obj: locfit/scb.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT scb.obj -MD -MP -MF "$(DEPDIR)/scb.Tpo" -c -o scb.obj `if test -f 'locfit/scb.c'; then $(CYGPATH_W) 'locfit/scb.c'; else $(CYGPATH_W) '$(srcdir)/locfit/scb.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/scb.Tpo" "$(DEPDIR)/scb.Po"; else rm -f "$(DEPDIR)/scb.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/scb.c' object='scb.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o scb.obj `if test -f 'locfit/scb.c'; then $(CYGPATH_W) 'locfit/scb.c'; else $(CYGPATH_W) '$(srcdir)/locfit/scb.c'; fi`
+
+scb_cons.o: locfit/scb_cons.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT scb_cons.o -MD -MP -MF "$(DEPDIR)/scb_cons.Tpo" -c -o scb_cons.o `test -f 'locfit/scb_cons.c' || echo '$(srcdir)/'`locfit/scb_cons.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/scb_cons.Tpo" "$(DEPDIR)/scb_cons.Po"; else rm -f "$(DEPDIR)/scb_cons.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/scb_cons.c' object='scb_cons.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o scb_cons.o `test -f 'locfit/scb_cons.c' || echo '$(srcdir)/'`locfit/scb_cons.c
+
+scb_cons.obj: locfit/scb_cons.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT scb_cons.obj -MD -MP -MF "$(DEPDIR)/scb_cons.Tpo" -c -o scb_cons.obj `if test -f 'locfit/scb_cons.c'; then $(CYGPATH_W) 'locfit/scb_cons.c'; else $(CYGPATH_W) '$(srcdir)/locfit/scb_cons.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/scb_cons.Tpo" "$(DEPDIR)/scb_cons.Po"; else rm -f "$(DEPDIR)/scb_cons.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/scb_cons.c' object='scb_cons.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o scb_cons.obj `if test -f 'locfit/scb_cons.c'; then $(CYGPATH_W) 'locfit/scb_cons.c'; else $(CYGPATH_W) '$(srcdir)/locfit/scb_cons.c'; fi`
+
+simul.o: locfit/simul.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT simul.o -MD -MP -MF "$(DEPDIR)/simul.Tpo" -c -o simul.o `test -f 'locfit/simul.c' || echo '$(srcdir)/'`locfit/simul.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/simul.Tpo" "$(DEPDIR)/simul.Po"; else rm -f "$(DEPDIR)/simul.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/simul.c' object='simul.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o simul.o `test -f 'locfit/simul.c' || echo '$(srcdir)/'`locfit/simul.c
+
+simul.obj: locfit/simul.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT simul.obj -MD -MP -MF "$(DEPDIR)/simul.Tpo" -c -o simul.obj `if test -f 'locfit/simul.c'; then $(CYGPATH_W) 'locfit/simul.c'; else $(CYGPATH_W) '$(srcdir)/locfit/simul.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/simul.Tpo" "$(DEPDIR)/simul.Po"; else rm -f "$(DEPDIR)/simul.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/simul.c' object='simul.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o simul.obj `if test -f 'locfit/simul.c'; then $(CYGPATH_W) 'locfit/simul.c'; else $(CYGPATH_W) '$(srcdir)/locfit/simul.c'; fi`
+
+solve.o: locfit/solve.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT solve.o -MD -MP -MF "$(DEPDIR)/solve.Tpo" -c -o solve.o `test -f 'locfit/solve.c' || echo '$(srcdir)/'`locfit/solve.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/solve.Tpo" "$(DEPDIR)/solve.Po"; else rm -f "$(DEPDIR)/solve.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/solve.c' object='solve.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o solve.o `test -f 'locfit/solve.c' || echo '$(srcdir)/'`locfit/solve.c
+
+solve.obj: locfit/solve.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT solve.obj -MD -MP -MF "$(DEPDIR)/solve.Tpo" -c -o solve.obj `if test -f 'locfit/solve.c'; then $(CYGPATH_W) 'locfit/solve.c'; else $(CYGPATH_W) '$(srcdir)/locfit/solve.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/solve.Tpo" "$(DEPDIR)/solve.Po"; else rm -f "$(DEPDIR)/solve.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/solve.c' object='solve.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o solve.obj `if test -f 'locfit/solve.c'; then $(CYGPATH_W) 'locfit/solve.c'; else $(CYGPATH_W) '$(srcdir)/locfit/solve.c'; fi`
+
+startlf.o: locfit/startlf.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT startlf.o -MD -MP -MF "$(DEPDIR)/startlf.Tpo" -c -o startlf.o `test -f 'locfit/startlf.c' || echo '$(srcdir)/'`locfit/startlf.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/startlf.Tpo" "$(DEPDIR)/startlf.Po"; else rm -f "$(DEPDIR)/startlf.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/startlf.c' object='startlf.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o startlf.o `test -f 'locfit/startlf.c' || echo '$(srcdir)/'`locfit/startlf.c
+
+startlf.obj: locfit/startlf.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT startlf.obj -MD -MP -MF "$(DEPDIR)/startlf.Tpo" -c -o startlf.obj `if test -f 'locfit/startlf.c'; then $(CYGPATH_W) 'locfit/startlf.c'; else $(CYGPATH_W) '$(srcdir)/locfit/startlf.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/startlf.Tpo" "$(DEPDIR)/startlf.Po"; else rm -f "$(DEPDIR)/startlf.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/startlf.c' object='startlf.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o startlf.obj `if test -f 'locfit/startlf.c'; then $(CYGPATH_W) 'locfit/startlf.c'; else $(CYGPATH_W) '$(srcdir)/locfit/startlf.c'; fi`
+
+strings.o: locfit/strings.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT strings.o -MD -MP -MF "$(DEPDIR)/strings.Tpo" -c -o strings.o `test -f 'locfit/strings.c' || echo '$(srcdir)/'`locfit/strings.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/strings.Tpo" "$(DEPDIR)/strings.Po"; else rm -f "$(DEPDIR)/strings.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/strings.c' object='strings.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o strings.o `test -f 'locfit/strings.c' || echo '$(srcdir)/'`locfit/strings.c
+
+strings.obj: locfit/strings.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT strings.obj -MD -MP -MF "$(DEPDIR)/strings.Tpo" -c -o strings.obj `if test -f 'locfit/strings.c'; then $(CYGPATH_W) 'locfit/strings.c'; else $(CYGPATH_W) '$(srcdir)/locfit/strings.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/strings.Tpo" "$(DEPDIR)/strings.Po"; else rm -f "$(DEPDIR)/strings.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/strings.c' object='strings.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o strings.obj `if test -f 'locfit/strings.c'; then $(CYGPATH_W) 'locfit/strings.c'; else $(CYGPATH_W) '$(srcdir)/locfit/strings.c'; fi`
+
+wdiag.o: locfit/wdiag.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT wdiag.o -MD -MP -MF "$(DEPDIR)/wdiag.Tpo" -c -o wdiag.o `test -f 'locfit/wdiag.c' || echo '$(srcdir)/'`locfit/wdiag.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/wdiag.Tpo" "$(DEPDIR)/wdiag.Po"; else rm -f "$(DEPDIR)/wdiag.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/wdiag.c' object='wdiag.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o wdiag.o `test -f 'locfit/wdiag.c' || echo '$(srcdir)/'`locfit/wdiag.c
+
+wdiag.obj: locfit/wdiag.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT wdiag.obj -MD -MP -MF "$(DEPDIR)/wdiag.Tpo" -c -o wdiag.obj `if test -f 'locfit/wdiag.c'; then $(CYGPATH_W) 'locfit/wdiag.c'; else $(CYGPATH_W) '$(srcdir)/locfit/wdiag.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/wdiag.Tpo" "$(DEPDIR)/wdiag.Po"; else rm -f "$(DEPDIR)/wdiag.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/wdiag.c' object='wdiag.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o wdiag.obj `if test -f 'locfit/wdiag.c'; then $(CYGPATH_W) 'locfit/wdiag.c'; else $(CYGPATH_W) '$(srcdir)/locfit/wdiag.c'; fi`
+
+weight.o: locfit/weight.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT weight.o -MD -MP -MF "$(DEPDIR)/weight.Tpo" -c -o weight.o `test -f 'locfit/weight.c' || echo '$(srcdir)/'`locfit/weight.c; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/weight.Tpo" "$(DEPDIR)/weight.Po"; else rm -f "$(DEPDIR)/weight.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/weight.c' object='weight.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o weight.o `test -f 'locfit/weight.c' || echo '$(srcdir)/'`locfit/weight.c
+
+weight.obj: locfit/weight.c
+ at am__fastdepCC_TRUE@	if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT weight.obj -MD -MP -MF "$(DEPDIR)/weight.Tpo" -c -o weight.obj `if test -f 'locfit/weight.c'; then $(CYGPATH_W) 'locfit/weight.c'; else $(CYGPATH_W) '$(srcdir)/locfit/weight.c'; fi`; \
+ at am__fastdepCC_TRUE@	then mv -f "$(DEPDIR)/weight.Tpo" "$(DEPDIR)/weight.Po"; else rm -f "$(DEPDIR)/weight.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	source='locfit/weight.c' object='weight.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o weight.obj `if test -f 'locfit/weight.c'; then $(CYGPATH_W) 'locfit/weight.c'; else $(CYGPATH_W) '$(srcdir)/locfit/weight.c'; fi`
 
 .cpp.o:
 @am__fastdepCXX_TRUE@	if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
@@ -448,6 +1372,20 @@ distclean-compile:
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+vari.o: locfit/vari.cpp
+ at am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT vari.o -MD -MP -MF "$(DEPDIR)/vari.Tpo" -c -o vari.o `test -f 'locfit/vari.cpp' || echo '$(srcdir)/'`locfit/vari.cpp; \
+ at am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/vari.Tpo" "$(DEPDIR)/vari.Po"; else rm -f "$(DEPDIR)/vari.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='locfit/vari.cpp' object='vari.o' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o vari.o `test -f 'locfit/vari.cpp' || echo '$(srcdir)/'`locfit/vari.cpp
+
+vari.obj: locfit/vari.cpp
+ at am__fastdepCXX_TRUE@	if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT vari.obj -MD -MP -MF "$(DEPDIR)/vari.Tpo" -c -o vari.obj `if test -f 'locfit/vari.cpp'; then $(CYGPATH_W) 'locfit/vari.cpp'; else $(CYGPATH_W) '$(srcdir)/locfit/vari.cpp'; fi`; \
+ at am__fastdepCXX_TRUE@	then mv -f "$(DEPDIR)/vari.Tpo" "$(DEPDIR)/vari.Po"; else rm -f "$(DEPDIR)/vari.Tpo"; exit 1; fi
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='locfit/vari.cpp' object='vari.obj' libtool=no @AMDEPBACKSLASH@
+ at AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ at am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o vari.obj `if test -f 'locfit/vari.cpp'; then $(CYGPATH_W) 'locfit/vari.cpp'; else $(CYGPATH_W) '$(srcdir)/locfit/vari.cpp'; fi`
 uninstall-info-am:
 
 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
@@ -499,7 +1437,7 @@ distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 
 distdir: $(DISTFILES)
-	$(mkdir_p) $(distdir)/$(top_srcdir) $(distdir)/lemon $(distdir)/lemon/bits $(distdir)/lemon/concepts
+	$(mkdir_p) $(distdir)/$(top_srcdir) $(distdir)/lemon $(distdir)/lemon/bits $(distdir)/lemon/concepts $(distdir)/locfit
 	@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
 	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
 	list='$(DISTFILES)'; for file in $$list; do \
@@ -528,9 +1466,9 @@ distdir: $(DISTFILES)
 	done
 check-am: all-am
 check: check-am
-all-am: Makefile $(LIBRARIES) $(PROGRAMS) $(HEADERS)
+all-am: Makefile $(LIBRARIES) $(PROGRAMS) $(SCRIPTS) $(HEADERS)
 installdirs:
-	for dir in "$(DESTDIR)$(bindir)"; do \
+	for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)"; do \
 	  test -z "$$dir" || $(mkdir_p) "$$dir"; \
 	done
 install: install-am
@@ -550,6 +1488,7 @@ install-strip:
 mostlyclean-generic:
 
 clean-generic:
+	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
 
 distclean-generic:
 	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
@@ -580,7 +1519,7 @@ info-am:
 
 install-data-am:
 
-install-exec-am: install-binPROGRAMS
+install-exec-am: install-binPROGRAMS install-binSCRIPTS
 
 install-info: install-info-am
 
@@ -605,24 +1544,24 @@ ps: ps-am
 
 ps-am:
 
-uninstall-am: uninstall-binPROGRAMS uninstall-info-am
+uninstall-am: uninstall-binPROGRAMS uninstall-binSCRIPTS \
+	uninstall-info-am
 
 .PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
 	clean-generic clean-noinstLIBRARIES ctags distclean \
 	distclean-compile distclean-generic distclean-tags distdir dvi \
 	dvi-am html html-am info info-am install install-am \
-	install-binPROGRAMS install-data install-data-am install-exec \
-	install-exec-am install-info install-info-am install-man \
-	install-strip installcheck installcheck-am installdirs \
-	maintainer-clean maintainer-clean-generic mostlyclean \
-	mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
-	tags uninstall uninstall-am uninstall-binPROGRAMS \
+	install-binPROGRAMS install-binSCRIPTS install-data \
+	install-data-am install-exec install-exec-am install-info \
+	install-info-am install-man install-strip installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
+	uninstall-am uninstall-binPROGRAMS uninstall-binSCRIPTS \
 	uninstall-info-am
 
-
-#sorting_hat_SOURCES = sorting_hat.cpp
-#sorting_hat_LDADD = libcufflinks.a libgc.a $(BOOST_THREAD_LIB) $(BAM_LIB)
-#sorting_hat_LDFLAGS =  $(BOOST_LDFLAGS) $(BAM_LDFLAGS) 
+.py:
+	(echo '#!$(PYTHON)'; sed '/^#!/d' $<) > $@
 
 #gtf_reads_SOURCES = gtf_reads.cpp
 #gtf_reads_LDADD = libcufflinks.a libgc.a $(BOOST_THREAD_LIB) $(BAM_LIB) 
diff --git a/src/abundances.cpp b/src/abundances.cpp
index 3391186..3ddc831 100644
--- a/src/abundances.cpp
+++ b/src/abundances.cpp
@@ -17,15 +17,21 @@
 #include <boost/numeric/ublas/vector_proxy.hpp>
 #include <boost/numeric/ublas/matrix.hpp>
 #include <boost/numeric/ublas/triangular.hpp>
+
+//#define BOOST_UBLAS_TYPE_CHECK 0
 #include <boost/numeric/ublas/lu.hpp>
+
 #include <boost/numeric/ublas/io.hpp>
 
 #include <boost/random/mersenne_twister.hpp>
 #include <boost/random/normal_distribution.hpp>
 #include <boost/random/variate_generator.hpp>
 #include <boost/math/constants/constants.hpp>
+#include <boost/math/tools/roots.hpp>
+#include <complex>
 
 #include "filters.h"
+#include "replicates.h"
 
 //#define USE_LOG_CACHE
 
@@ -62,10 +68,14 @@ AbundanceStatus AbundanceGroup::status() const
 {
 	foreach(shared_ptr<Abundance> ab, _abundances)
 	{
-		if (ab->status() != NUMERIC_OK)
+		if (ab->status() == NUMERIC_FAIL)
 		{
 			return NUMERIC_FAIL;
 		}
+        if (ab->status() == NUMERIC_LOW_DATA)
+		{
+			return NUMERIC_LOW_DATA;
+		}
 	}
 	return NUMERIC_OK;
 }
@@ -92,6 +102,17 @@ double AbundanceGroup::mass_fraction() const
 	return mass;
 }
 
+double AbundanceGroup::mass_variance() const
+{
+    double mass_var = 0;
+	
+	foreach(shared_ptr<Abundance> ab, _abundances)
+	{
+		mass_var += ab->mass_variance();
+	}
+	return mass_var;
+}
+
 double AbundanceGroup::FPKM() const
 {
 	double fpkm = 0;
@@ -153,7 +174,7 @@ void AbundanceGroup::filter_group(const vector<bool>& to_keep,
 		}
 	}
 	
-	filtered_group = AbundanceGroup(new_ab, new_cov);
+	filtered_group = AbundanceGroup(new_ab, new_cov, _max_mass_variance);
 }
 
 void AbundanceGroup::get_transfrags(vector<shared_ptr<Abundance> >& transfrags) const
@@ -283,7 +304,7 @@ double AbundanceGroup::effective_length() const
 	return eff_len;
 }
 
-void AbundanceGroup::calculate_counts(const vector<MateHit>& alignments, 
+void AbundanceGroup::calculate_counts(const vector<MateHit>& alignments,
                                       const vector<shared_ptr<Abundance> >& transcripts)
 {
 	size_t M = alignments.size();
@@ -310,11 +331,11 @@ void AbundanceGroup::calculate_counts(const vector<MateHit>& alignments,
 		}
 		if (mapped)
         {
-			//X_g += (1.0 - alignments[i].error_prob());
             shared_ptr<ReadGroupProperties const> rg_props = alignments[i].read_group_props();
             //assert (parent != NULL);
             pair<map<shared_ptr<ReadGroupProperties const>, double>::iterator, bool> inserted;
             inserted = count_per_replicate.insert(make_pair(rg_props, 0.0));
+            
             double more_mass = alignments[i].collapse_mass();
             inserted.first->second += more_mass;
         }
@@ -323,30 +344,64 @@ void AbundanceGroup::calculate_counts(const vector<MateHit>& alignments,
     double avg_X_g = 0.0;
     double avg_mass_fraction = 0.0;
     
+    // as long as all the read groups share the same dispersion model (currently true)
+    // then all the variances from each read group will be the same, so this
+    // averaging step isn't strictly necessary.  Computing it this way is simply
+    // convenient.
+    vector<double> avg_mass_variances(N, 0.0);
+    
+    double max_mass_var = 0.0;
     for (map<shared_ptr<ReadGroupProperties const>, double>::iterator itr = count_per_replicate.begin();
          itr != count_per_replicate.end();
          ++itr)
     {
-        avg_X_g += itr->second;
-        assert (itr->first->total_map_mass() != 0.0);
         shared_ptr<ReadGroupProperties const> rg_props = itr->first;
-        avg_mass_fraction += (itr->second / rg_props->total_map_mass());
+        double scaled_mass = rg_props->scale_mass(itr->second);
+        double scaled_total_mass = rg_props->scale_mass(rg_props->normalized_map_mass());
+        avg_X_g += scaled_mass;
+        shared_ptr<MassDispersionModel const> disperser = rg_props->mass_dispersion_model();
+        for (size_t j = 0; j < N; ++j)
+        {
+            double scaled_variance = disperser->scale_mass_variance(scaled_mass * _abundances[j]->gamma());
+            avg_mass_variances[j] += scaled_variance;
+        }
+        assert (disperser->scale_mass_variance(scaled_mass) != 0 || scaled_mass == 0); 
+         max_mass_var += disperser->scale_mass_variance(scaled_mass);
+        assert (scaled_total_mass != 0.0);
+        avg_mass_fraction += (scaled_mass / scaled_total_mass);
     }
     
+    // Set the maximum mass variance in case we get an identifiability failure
+    // and need to bound the group expression.
+    if (!count_per_replicate.empty())
+        max_mass_var /= count_per_replicate.size();
+    
+    
     double num_replicates = count_per_replicate.size();
     
     if (num_replicates)
     {
         avg_X_g /= num_replicates;
         avg_mass_fraction /= num_replicates;
+        for (size_t j = 0; j < N; ++j)
+        {
+            avg_mass_variances[j] /= num_replicates;
+        }
     }
     
+    assert (max_mass_var != 0 || avg_X_g == 0);
+    max_mass_variance(max_mass_var);
+    
 	for (size_t j = 0; j < N; ++j)
 	{
 		_abundances[j]->num_fragments(_abundances[j]->gamma() * avg_X_g);
+        
         double j_avg_mass_fraction = _abundances[j]->gamma() * avg_mass_fraction;
         _abundances[j]->mass_fraction(j_avg_mass_fraction);
-        if (j_avg_mass_fraction)
+        
+        _abundances[j]->mass_variance(avg_mass_variances[j]);
+        
+        if (j_avg_mass_fraction > 0)
         {
             double FPKM = j_avg_mass_fraction * 1000000000/ _abundances[j]->effective_length();
             _abundances[j]->FPKM(FPKM);
@@ -354,35 +409,565 @@ void AbundanceGroup::calculate_counts(const vector<MateHit>& alignments,
         else 
         {
             _abundances[j]->FPKM(0);
+            _abundances[j]->mass_variance(0);
+            _abundances[j]->mass_fraction(0);
+        }
+	}
+}
+
+
+int total_cond_prob_calls = 0;
+void collapse_equivalent_hits(const vector<MateHit>& alignments,
+                              vector<shared_ptr<Abundance> >& transcripts,
+                              vector<shared_ptr<Abundance> >& mapped_transcripts,
+                              vector<MateHit>& nr_alignments,
+                              vector<double>& log_conv_factors)
+{
+    int N = transcripts.size();
+	int M = alignments.size();
+    
+    nr_alignments.clear();
+    
+	vector<vector<char> > compatibilities(N, vector<char>(M,0));
+	compute_compatibilities(transcripts, alignments, compatibilities);
+    
+    vector<vector<double> > cached_cond_probs (M, vector<double>());
+    
+    vector<bool> replaced(M, false);
+    int num_replaced = 0;
+    
+    vector<BiasCorrectionHelper> bchs;
+    for (size_t j = 0; j < N; ++j)
+    {
+        bchs.push_back(BiasCorrectionHelper(transcripts[j]->transfrag()));   
+    }
+    
+    for(int i = 0 ; i < M; ++i)
+    {
+        vector<double> cond_probs_i(N,0);
+        if (replaced[i] == true)
+            continue;
+        
+        if (cached_cond_probs[i].empty())
+        {
+            for (int j = 0; j < N; ++j)
+            {
+                shared_ptr<Scaffold> transfrag = transcripts[j]->transfrag();
+                
+                if (compatibilities[j][i]==1)
+                {
+                    total_cond_prob_calls++;
+                    cond_probs_i[j] = bchs[j].get_cond_prob(alignments[i]);
+                }
+                
+            }
+            cached_cond_probs[i] = cond_probs_i;
+        }
+        else
+        {
+            cond_probs_i = cached_cond_probs[i];
         }
+        
+        MateHit* curr_align = NULL;
+        
+        nr_alignments.push_back(alignments[i]);
+        curr_align = &nr_alignments.back();
+        log_conv_factors.push_back(0);
+        
+        if (alignments[i].is_multi()) // don't reduce other hits into multihits
+            continue;
+        
+        bool seen_olap = false;
+        
+        for(int k = i + 1 ; k < M; ++k)
+        {
+            if (replaced[k] || alignments[k].is_multi() || alignments[i].read_group_props() != alignments[k].read_group_props())
+                continue;
+            if (!::overlap_in_genome(curr_align->left(), curr_align->right(),
+                                     alignments[k].left(), alignments[k].right()))
+            {
+                if (seen_olap) 
+                    break;
+                else
+                    continue;
+            }
+            else
+            {
+                seen_olap = true;   
+            }
+            
+            vector<double>* cond_probs_k;
+            double last_cond_prob = -1;
+            
+            bool equiv = true;
+            
+            if (cached_cond_probs[k].empty())
+            {
+                cached_cond_probs[k] = vector<double>(N, 0.0);
+                cond_probs_k = &cached_cond_probs[k];
+                for (int j = 0; j < N; ++j)
+                {
+                    shared_ptr<Scaffold> transfrag = transcripts[j]->transfrag();
+                    
+                    if (compatibilities[j][k]==1)
+                    {
+                        total_cond_prob_calls++;
+                        (*cond_probs_k)[j] = bchs[j].get_cond_prob(alignments[k]);
+                    }
+                }
+                //cached_cond_probs[k] = cond_probs_k;
+            }
+            else
+            {
+                cond_probs_k = &cached_cond_probs[k];
+            }
+               
+            
+            for (int j = 0; j < N; ++j)
+            {
+                if ((*cond_probs_k)[j] != 0 && cond_probs_i[j] != 0)
+                {
+                    double ratio =  (*cond_probs_k)[j] / cond_probs_i[j];
+                    if (last_cond_prob == -1)
+                    {
+                        //assert(ratio < 5);
+                        last_cond_prob = ratio;
+                    }
+                    else
+                    {
+                        if (last_cond_prob != ratio)
+                        {
+                            equiv = false;
+                            break;
+                        }
+                    }
+                }
+                else if ((*cond_probs_k)[j] == 0 && cond_probs_i[j] == 0)
+                {
+                    // just do nothing in this iter.
+                    // last_cond_prob = 0.0;
+                }
+                else
+                {
+                    equiv = false;
+                    break;
+                }
+            }
+            
+            // cond_prob_i vector is a scalar multiple of cond_prob_k, so we
+            // can collapse k into i via the mass.
+            if (equiv && last_cond_prob > 0.0)
+            {
+                assert (last_cond_prob > 0);
+                //double mass_muliplier = sqrt(last_cond_prob);
+                double mass_multiplier = log(last_cond_prob);
+                //assert(last_cond_prob < 5);
+                assert (!isinf(mass_multiplier) && !isnan(mass_multiplier));
+                log_conv_factors[log_conv_factors.size() - 1] += mass_multiplier; 
+                replaced[k] = true;
+                cached_cond_probs[k].clear();
+                vector<double>(cached_cond_probs[k]).swap(cached_cond_probs[k]);
+                num_replaced++;
+                double more_mass = alignments[k].common_scale_mass() * alignments[k].collapse_mass() ;
+                //double more_mass = alignments[k].common_scale_mass();
+                curr_align->incr_collapse_mass(more_mass);
+            }
+        }
+    }
+    
+    N = transcripts.size();
+	//M = nr_alignments.size();
+        
+	for (int j = 0; j < N; ++j) 
+    {
+		shared_ptr<Scaffold> transfrag = transcripts[j]->transfrag();
+		vector<double>& cond_probs = *(new vector<double>(nr_alignments.size(),0));
+		
+		BiasCorrectionHelper& bch = bchs[j];
+		
+        size_t last_cond_prob_idx = 0;
+		for(int i = 0 ; i < M; ++i)
+		{
+            if (!cached_cond_probs[i].empty())
+            {
+                if (compatibilities[j][i]==1)
+                {
+                    assert (cached_cond_probs[i].size() > j);
+                    cond_probs[last_cond_prob_idx] = cached_cond_probs[i][j];
+                }
+                last_cond_prob_idx++;
+            }
+        }
+		
+        assert (last_cond_prob_idx == nr_alignments.size());
+        
+		transcripts[j]->effective_length(bch.get_effective_length());
+		transcripts[j]->cond_probs(&cond_probs);
+		
+		if (bch.is_mapped()) 
+			mapped_transcripts.push_back(transcripts[j]);
 	}
+    if (nr_alignments.size())
+    {
+        verbose_msg("\nReduced %lu frags to %lu (%lf percent)\n", alignments.size(), nr_alignments.size(), 100.0 * nr_alignments.size()/(double)alignments.size());
+    }
 }
 
+#define PERFORM_EQUIV_COLLAPSE 1
+
 void AbundanceGroup::calculate_abundance(const vector<MateHit>& alignments)
 {
+    
+//    foreach (const string& s, gene_id())
+//    {
+//        if (s == "XLOC_000041")
+//        {
+//            int a = 4;
+//        }
+//    }
 	vector<shared_ptr<Abundance> > transcripts;
 	get_transfrags(transcripts);
 	vector<shared_ptr<Abundance> > mapped_transcripts; // This collects the transcripts that have alignments mapping to them
 	
 	vector<MateHit> nr_alignments;
 	collapse_hits(alignments, nr_alignments);
+    
+    vector<MateHit> non_equiv_alignments;
+    vector<double> log_conv_factors;
+    if (cond_prob_collapse)
+    {
+        collapse_equivalent_hits(nr_alignments, transcripts, mapped_transcripts, non_equiv_alignments, log_conv_factors);
+        assert (non_equiv_alignments.size() == log_conv_factors.size());
+        nr_alignments.clear();
+    }
+    else
+    {
+        non_equiv_alignments = nr_alignments;
+        log_conv_factors = vector<double>(nr_alignments.size(), 0);
+        compute_cond_probs_and_effective_lengths(non_equiv_alignments, transcripts, mapped_transcripts);
+    }
+        
+	calculate_gammas(non_equiv_alignments, log_conv_factors, transcripts, mapped_transcripts);		
 	
-	compute_cond_probs_and_effective_lengths(nr_alignments, transcripts, mapped_transcripts);
-	
-	calculate_gammas(nr_alignments, transcripts, mapped_transcripts);		
-	
+    //non_equiv_alignments.clear();
+	//collapse_hits(alignments, nr_alignments);
     // This will also compute the transcript level FPKMs
-    calculate_counts(nr_alignments, transcripts);  
+    calculate_counts(non_equiv_alignments, transcripts);  
 
+	if(corr_multi && !final_est_run)
+	{
+		update_multi_reads(non_equiv_alignments, mapped_transcripts);
+	}
+	
 	if (final_est_run) // Only on last estimation run
 	{
         calculate_conf_intervals();
         calculate_kappas();
     }
+    
+    //fprintf(stderr, "Total calls to get_cond_prob = %d\n", total_cond_prob_calls);
 }
 
-void AbundanceGroup::calculate_conf_intervals()
+void AbundanceGroup::update_multi_reads(const vector<MateHit>& alignments, vector<shared_ptr<Abundance> > transcripts)
+{
+	size_t M = alignments.size();
+	size_t N = transcripts.size();
+	
+	if (transcripts.empty())
+		return;
+    
+    for (size_t i = 0; i < M; ++i)
+	{
+		if (alignments[i].is_multi())
+		{
+			double expr = 0.0;
+			for (size_t j = 0; j < N; ++j)
+			{
+				expr += _abundances[j]->cond_probs()->at(i) * _abundances[j]->FPKM() * _abundances[j]->effective_length();
+			}
+			alignments[i].read_group_props()->multi_read_table()->add_expr(alignments[i], expr);
+		}
+	}
+}
+
+struct BetaCubic
+{
+    
+    BetaCubic (long double a, long double b, long double c):
+        A(a), B(b), C(c) 
+    {
+        _3_coeff = C/B;
+        _2_coeff = (4 * C / B) - (4 * A*C/(B*B));
+        _1_coeff = (5 * A*A*C / (B*B*B)) - (10 * A*C/(B*B)) - A/(A-B) + 5*C/B;
+        _0_coeff = 1 + (2 * C/B) - (6*A*C/(B*B)) + (6*A*A*C)/(B*B*B) - (2*A*A*A*C)/(B*B*B*B);
+    }
+    long double operator()(long double beta)
+    {
+        long double v = 0.0;
+        v += powl(beta,3.0) * _3_coeff;
+        v += powl(beta,2.0) * _2_coeff;
+        v += beta, _1_coeff;
+        v += _0_coeff;
+        return v;
+    }
+    
+private:
+    long double A;
+    long double B;
+    long double C;
+    long double _3_coeff;
+    long double _2_coeff;
+    long double _1_coeff;
+    long double _0_coeff;
+};
+
+long double solve_beta(long double A, long double B, long double C)
+{
+    
+    complex<long double> t1 = 2.0*A*A*A*powl(B,12) + 
+                     24.0 * A*A*A*powl(B,10)*C + 
+                     51.0*A*A*A*powl(B,8)*C*C + 
+                     2.0*A*A*A*powl(B,6)*C*C*C - 
+                     33.0*A*A*powl(B,11)*C - 
+                     138.0*A*A*powl(B,9)*C*C - 
+                     6.0*A*A*powl(B,7)*C*C*C;
+    
+    assert(!isnan(t1.real()));
+    
+    complex<long double> t7 = 9.0*A*powl(B,12)*C + 
+                     123.0*A*powl(B,10)*C*C +
+                     6.0*A*powl(B,8)*C*C*C - 
+                     36.0*powl(B,11)*C*C - 
+                     2.0*powl(B,9)*C*C*C;
+    assert(!isnan(t7.real()));
+    
+    complex<long double> t2 = -A*A*powl(B,8) - 
+                     8.0*A*A*powl(B,6)*C - 
+                     A*A*powl(B,4)*C*C + 
+                     11.0*A*powl(B,7)*C + 
+                     2.0*A*powl(B,5)*C*C - 
+                     3.0*powl(B,8)*C - 
+                     powl(B,6)*C*C;
+    assert(!isnan(t2.real()));
+    //assert(powl(t2,3) >= 0);
+    
+    complex<long double> t3 = t1 + t7;
+    assert(!isnan(t3.real()));
+    complex<long double> p_ab = 4 * t2.real()*t2.real()*t2.real() + t3.real()*t3.real();
+    //complex<long double> t4 = sqrtl(p_ab);
+    complex<long double> cp_ab(p_ab);
+    complex<long double> t4 = std::sqrt(cp_ab);
+    //assert(!isnan(t4));
+    
+    
+    complex<long double> t6 = std::pow((t1 + t4 + t7), 1.0/3.0);
+    assert(!isnan(t6.real()));
+    
+    complex<long double> t10 = cbrtl(2.0)*t2 / (3*B*B*B*C*t6);
+    assert(!isnan(t10.real()));
+    
+    complex<long double> t8 = (-A*powl(B,4) - 4*A*B*B*C + 4*B*B*B*C) / (3*B*B*B*C);
+    assert(!isnan(t8.real()));
+    
+    complex<long double> t9 = 1.0/(3*cbrtl(2)*B*B*B*C);
+    assert(!isnan(t9.real()));
+    
+    complex<long double> beta = t9 * t6 - t10 - t8; 
+    assert(!isnan(beta.real()));
+    
+//    fprintf(stderr, "T1 = %Lg\n", t1.real());
+//
+//    fprintf(stderr, "T2 = %Lg\n", t2.real());
+//
+//    fprintf(stderr, "T3 = %Lg\n", t3.real());
+//
+//    fprintf(stderr, "T4 = %Lg\n", t4.real());
+//
+//    fprintf(stderr, "T6 = %Lg\n", t6.real());
+//    
+//    fprintf(stderr, "T7 = %Lg\n", t7.real());
+//
+//    fprintf(stderr, "T8 = %Lg\n", t8.real());
+//    
+//    fprintf(stderr, "T9 = %Lg\n", t9.real());
+//
+//    fprintf(stderr, "T10 = %Lg\n", t10.real());
+//    
+//    fprintf(stderr, "beta = (real : %Lg, imag : %Lg\n", beta.real(), beta.imag());
+
+    
+    return beta.real();
+}
+
+bool compute_fpkm_variance(long double& variance,
+                             double gamma_t, 
+                             double psi_t, 
+                             double X_g, 
+                             double V_X_g_t,
+                             double l_t,
+                             double M)
 {
+    if (l_t == 0)
+    {
+        //assert(X_g * gamma_t == 0);
+        return 0;
+    }
+//    if (V_X_g_t < X_g)
+//        V_X_g_t = X_g;
+//    long double A = 1000000000.0 * X_g * gamma_t;
+//    A /= (l_t * M);
+//    
+//    long double B = 1000000000.0 / (l_t * M);
+//    B *= B;
+//    B *= V_X_g_t;
+//    
+//    long double C = 1000000000.0 * X_g / (l_t * M);
+//    C *= C;
+
+    long double A = X_g * gamma_t;
+    
+    long double B = V_X_g_t;
+    
+    long double C = X_g * X_g;
+    
+    variance = 0.0;
+    bool numeric_ok = true;
+    
+    long double dispersion = V_X_g_t - (X_g * gamma_t);
+    
+    if (dispersion < -1 || abs(dispersion) < 1)
+    {
+        // default to poisson dispersion
+        long double psi_var = X_g;
+        psi_var *= psi_var;
+        psi_var *= psi_t;
+        // we multiply A with the constants here to make things work out 
+        // at the end of the routine when we multiply by the square of those
+        // constants
+        variance = A + psi_var;
+        //printf("Warning: overdispersion too small to warrant NB, reverting to poisson\n");
+        //printf("\t X_g_gamma_t = %lg, V_X_g_t = %lg\n", X_g * gamma_t, V_X_g_t);
+        //printf("\t A = %Lg, B = %Lg\n", A, B);
+    }
+    else // there's some detectable overdispersion here, use mixture of negative binomials
+    {
+        if (psi_t == 0) 
+        {
+            //printf("Warning: Counts are overdispersed, using single-isoform NB distribution\n");
+            // default to regular negative binomial case.
+            //assert (gamma_t == 1.0);
+            //double FPKM = 1000000000.0 * X_g * gamma_t / (l_t * M);
+            variance = V_X_g_t;
+        }
+        else
+        {
+            //printf("Warning: Counts are overdispersed, using multi-isoform NB distribution\n");
+            //long double max_doub = numeric_limits<long double>::max();
+            //assert (psi_t < gamma_t * gamma_t);
+            C*= psi_t;
+            long double r = (A * A) / (B - A);
+            
+            long double beta = solve_beta(A,B,C);
+        
+            long double alpha = 1.0 - (A/(A-B)) * beta;
+            
+            long double mean = r * beta / (alpha - 1.0);
+            
+            long double FPKM = 1000000000.0 * X_g * gamma_t / (l_t * M);
+            
+            variance = r * (alpha + r - 1.0) * beta * (alpha + beta - 1);
+            variance /= (alpha - 2.0) * (alpha - 1.0) * (alpha - 1.0);
+            
+            if (beta <= 0)
+            {
+                //printf ("Warning: beta for is %Lg\n", beta);
+                numeric_ok = false;
+            }
+            if (alpha <= 0)
+            {
+                //printf("Warning: alpha for is %Lg\n", alpha);
+                //printf("\t A = %Lg, B = %Lg\n", A, B);
+                //printf("\t mean = %Lg, variance = %Lg\n", mean, variance);
+                //printf("\t X_g_gamma_t = %lg, V_X_g_t = %lg\n", X_g * gamma_t, V_X_g_t);
+                numeric_ok = false;
+            }
+            
+            //assert (abs(FPKM - mean) < 1e-3);
+        }
+    }
+    
+    double mean = A * (1000000000.0 / (l_t *M));
+    variance *= ((1000000000.0 / (l_t *M)))*((1000000000.0 / (l_t *M)));
+    assert (!isinf(variance) && !isnan(variance));
+    //printf("\t mean = %lg, variance = %lg\n", (double)mean, (double)variance);
+//    if (variance < mean)
+//    {
+//        printf ("Warning: mean > variance!\n");
+//        
+//    }
+    assert (!isinf(variance) && !isnan(variance));
+    assert (variance != 0 || A == 0);
+    return numeric_ok;
+}
+
+bool compute_fpkm_group_variance(long double& variance,
+                                 const vector<double>& gammas, 
+                                 const ublas::matrix<double>& psis, 
+                                 double X_g, 
+                                 const vector<double>& V_X_gs,
+                                 const vector<double>& ls,
+                                 double M)
+{
+    size_t len = gammas.size();
+    if (len == 1)
+        return compute_fpkm_variance(variance, gammas.front(), 0.0, X_g, V_X_gs.front(), ls.front(), M);
+    
+    double total_var = 0.0;
+    bool numeric_ok = true;
+    for (size_t i = 0; i < len; ++i)
+    {
+        bool ok = true;
+        long double var = 0.0;
+        ok = compute_fpkm_variance(var, gammas[i], psis(i,i), X_g, V_X_gs[i], ls[i], M);
+        total_var += var;
+        if (!ok)
+            numeric_ok = false;
+    }
+    
+    double cov = 0.0;
+    
+    for (size_t i = 0; i < len; ++i)
+    {
+        for (size_t j = 0; j < len; ++j)
+        {
+            if (ls[i] && ls[j])
+            {
+                assert(!isnan(psis(i,j)));
+                double L = ls[i] * ls[j];
+                assert(!isnan(L)); 
+                if (L != 0.0)
+                {
+                    double g = psis(i,j) / L;
+                    cov += g;
+                }
+            }
+        }    
+    }
+    double C = (1000000000.0 * X_g / M);
+    C *= C;
+    C *= cov;
+             
+    //double grp_var = compute_fpkm_variance(gamma_t, psi_t, X_g, V_X_g_t, 1.0, M); 
+    //assert (grp_var == total_var);
+    
+    variance = total_var + cov;
+    assert (!isinf(variance) && !isnan(variance));
+
+    return numeric_ok;
+}
+
+void AbundanceGroup::calculate_conf_intervals()
+{        
 	if (status() == NUMERIC_OK)
 	{
 		// This will compute the transcript level FPKM confidence intervals
@@ -390,20 +975,25 @@ void AbundanceGroup::calculate_conf_intervals()
 		{
 			if (_abundances[j]->effective_length() > 0.0 && mass_fraction() > 0)
 			{
-                double iso_fpkm_var = 0.0;
+                assert (!isnan(_gamma_covariance(j,j)));
                 
-                double norm_frag_density = 1000000000;
-                norm_frag_density /= _abundances[j]->effective_length();
-                
-                norm_frag_density *= mass_fraction();
-                iso_fpkm_var = norm_frag_density * _abundances[j]->gamma();
-                iso_fpkm_var += norm_frag_density * norm_frag_density * _gamma_covariance(j,j);
+                long double fpkm_var = 0.0;
+                bool numerics_ok = compute_fpkm_variance(fpkm_var,
+                                                        _abundances[j]->gamma(),
+                                                        _gamma_covariance(j,j),
+                                                        num_fragments(),
+                                                        _abundances[j]->mass_variance(),
+                                                        _abundances[j]->effective_length(),
+                                                        num_fragments()/mass_fraction());
+                if (numerics_ok == false)
+                    _abundances[j]->status(NUMERIC_LOW_DATA);
                 
-				double FPKM_hi = _abundances[j]->FPKM() + 2 * sqrt(iso_fpkm_var);
-				double FPKM_lo = max(0.0, _abundances[j]->FPKM() - 2 * sqrt(iso_fpkm_var));
+				double FPKM_hi = _abundances[j]->FPKM() + 2 * sqrt(fpkm_var);
+				double FPKM_lo = max(0.0, (double)(_abundances[j]->FPKM() - 2 * sqrt(fpkm_var)));
+				assert (FPKM_lo <= _abundances[j]->FPKM() && _abundances[j]->FPKM() <= FPKM_hi);
 				ConfidenceInterval conf(FPKM_lo, FPKM_hi);
 				_abundances[j]->FPKM_conf(conf);
-				_abundances[j]->FPKM_variance(iso_fpkm_var);
+				_abundances[j]->FPKM_variance(fpkm_var);
 			}
 			else
 			{
@@ -430,27 +1020,30 @@ void AbundanceGroup::calculate_conf_intervals()
 	}
 	else
 	{
-		double max_transfrag_FPKM_hi = 0;
-		double min_transfrag_FPKM_hi = numeric_limits<double>::max();
-        
+		double sum_transfrag_FPKM_hi = 0;
+        double max_fpkm = 0.0;
+        double min_fpkm = 1e100;
 		foreach(shared_ptr<Abundance> pA, _abundances)
 		{
 			double FPKM_hi;
 			double FPKM_lo;
 			if (pA->effective_length() > 0)
 			{
-                // FIXME: correct this
-                double fpkm_coeff = mass_fraction();
-                fpkm_coeff *= 1000000000;
-                fpkm_coeff /= pA->effective_length();
-                double fpkm_high = fpkm_coeff * gamma();
+                double norm_frag_density = 1000000000;
+                norm_frag_density /= pA->effective_length();
+                
+                norm_frag_density *= mass_fraction();
+                double fpkm_high = norm_frag_density;
+                
                 double var_fpkm = fpkm_high; 
-				
+                
 				FPKM_hi = fpkm_high + 2 * sqrt(var_fpkm);
 				FPKM_lo = 0.0;
 				ConfidenceInterval conf(FPKM_lo, FPKM_hi);
+				assert (FPKM_lo <= pA->FPKM() && pA->FPKM() <= FPKM_hi);
 				pA->FPKM_conf(conf);
                 pA->FPKM_variance(var_fpkm);
+				max_fpkm = max(sum_transfrag_FPKM_hi, FPKM_hi);
 			}
 			else
 			{
@@ -460,54 +1053,64 @@ void AbundanceGroup::calculate_conf_intervals()
 				pA->FPKM_conf(conf);
                 pA->FPKM_variance(0.0);
 			}
-			max_transfrag_FPKM_hi = max(max_transfrag_FPKM_hi, FPKM_hi);
-			min_transfrag_FPKM_hi = min(min_transfrag_FPKM_hi, FPKM_hi);
-				
+            
 		}
-		
+		calculate_FPKM_variance();
 		// In the case of a numeric failure, the groups error bars need to be 
 		// set such that 
-		FPKM_conf(ConfidenceInterval(0, max_transfrag_FPKM_hi));
+		FPKM_conf(ConfidenceInterval(0.0, max_fpkm + 2 * sqrt(FPKM_variance())));
+        
 	}
 }
 
-
 void AbundanceGroup::calculate_FPKM_variance()
 {
-	if (mass_fraction() == 0)
+	if (mass_fraction() == 0 || effective_length() == 0)
 	{
 		_FPKM_variance = 0.0;
 		return;
 	}
     
-    double var_cumul_gamma = 0;
-    for (size_t i = 0; i < _abundances.size(); ++i)
+    vector<double> gammas;
+    vector<double> ls;
+    vector<double> V_X_gs;
+    
+    for (size_t j = 0; j < _abundances.size(); ++j)
     {
-        for (size_t j = 0; j < _abundances.size(); ++j)
+        gammas.push_back(_abundances[j]->gamma());
+        ls.push_back(_abundances[j]->effective_length());
+        V_X_gs.push_back(_abundances[j]->mass_variance());
+    }
+    
+    if (status() == NUMERIC_OK)
+    {   
+        long double var = 0.0;
+        bool numeric_ok = compute_fpkm_group_variance(var,
+                                                      gammas,
+                                                      _gamma_covariance,
+                                                      num_fragments(),
+                                                      V_X_gs,
+                                                      ls,
+                                                      num_fragments()/mass_fraction());
+        _FPKM_variance = var;
+    }
+    else
+    {
+        long double max_var = 0.0;
+        for (size_t i = 0; i < _abundances.size(); ++i)
         {
-            if (_abundances[i]->effective_length() && _abundances[j]->effective_length())
-            {
-                assert(!isnan(_gamma_covariance(i,j)));
-                double L = _abundances[i]->effective_length() * _abundances[j]->effective_length();
-                assert(!isnan(L)); 
-                if (L != 0.0)
-                {
-                    double g = _gamma_covariance(i,j) / L;
-                    var_cumul_gamma += g;
-                }
-            }
-        }    
+            bool ok = true;
+            long double var = 0.0;
+            ok = compute_fpkm_variance(var, 1.0, 0.0, num_fragments(), max_mass_variance(), ls[i], num_fragments()/mass_fraction());
+            max_var = max(max_var,var);
+        }
+        _FPKM_variance = max_var;
+        assert (_FPKM_variance != 0 || FPKM() == 0);
     }
     
-    double mass_coeff = (1000000000 * mass_fraction());
-	//double left = (mass_coeff * gamma());
-    double eff_len = effective_length(); 
-    //double right = (mass_coeff * mass_coeff * var_cumul_gamma);
-    _FPKM_variance = ((mass_coeff * gamma()) / eff_len) + (mass_coeff * mass_coeff * var_cumul_gamma);
     assert (!isinf(_FPKM_variance) && !isnan(_FPKM_variance));
 }
 
-
 void AbundanceGroup::compute_cond_probs_and_effective_lengths(const vector<MateHit>& alignments,
 															  vector<shared_ptr<Abundance> >& transcripts,
 															  vector<shared_ptr<Abundance> >& mapped_transcripts)
@@ -527,7 +1130,10 @@ void AbundanceGroup::compute_cond_probs_and_effective_lengths(const vector<MateH
 		for(int i = 0 ; i < M; ++i)
 		{
 			if (compatibilities[j][i]==1)
+            {
+                total_cond_prob_calls++;
 				cond_probs[i] = bch.get_cond_prob(alignments[i]);
+            }
 		}
 		
 		transcripts[j]->effective_length(bch.get_effective_length());
@@ -541,6 +1147,7 @@ void AbundanceGroup::compute_cond_probs_and_effective_lengths(const vector<MateH
 // FIXME: This function doesn't really need to copy the transcripts out of 
 // the cluster.  Needs refactoring
 bool AbundanceGroup::calculate_gammas(const vector<MateHit>& nr_alignments, 
+                                      const vector<double>& log_conv_factors,
 									  const vector<shared_ptr<Abundance> >& transcripts, 
 									  const vector<shared_ptr<Abundance> >& mapped_transcripts)
 {
@@ -558,13 +1165,14 @@ bool AbundanceGroup::calculate_gammas(const vector<MateHit>& nr_alignments,
 	
 	vector<double> gammas;
 	
-	asm_verbose( "Calculating intial MLE\n");
+	verbose_msg( "Calculating intial MLE\n");
 	
-	gamma_mle(mapped_transcripts,
-			  nr_alignments,
-			  gammas);
+	AbundanceStatus mle_success = gamma_mle(mapped_transcripts,
+                                                  nr_alignments,
+                                                  log_conv_factors,
+                                                  gammas);
 	
-	asm_verbose( "Tossing likely garbage isoforms\n");
+	verbose_msg( "Tossing likely garbage isoforms\n");
 	
 	for (size_t i = 0; i < gammas.size(); ++i)
 	{
@@ -574,9 +1182,17 @@ bool AbundanceGroup::calculate_gammas(const vector<MateHit>& nr_alignments,
 		}
 	}
 	
+    double locus_mass = 0.0;
+   
+    for (size_t i = 0; i < nr_alignments.size(); ++i)
+    {
+        const MateHit& alignment = nr_alignments[i];
+        locus_mass += alignment.collapse_mass();
+    }
+    
 	vector<shared_ptr<Abundance> > filtered_transcripts = mapped_transcripts;
 	vector<double> filtered_gammas = gammas;
-	filter_junk_isoforms(filtered_transcripts, filtered_gammas);
+	filter_junk_isoforms(filtered_transcripts, filtered_gammas, mapped_transcripts, locus_mass);
 	
 	if (filtered_transcripts.empty())
 	{
@@ -590,14 +1206,17 @@ bool AbundanceGroup::calculate_gammas(const vector<MateHit>& nr_alignments,
 		return true;
 	}
 	
-	filtered_gammas.clear();
-	
-	asm_verbose( "Revising MLE\n");
-	
-	gamma_mle(filtered_transcripts,
-			  nr_alignments,
-			  filtered_gammas);
-	
+    if (filtered_transcripts.size() != mapped_transcripts.size())
+    {    
+        filtered_gammas.clear();
+        
+        verbose_msg( "Revising MLE\n");
+        
+        mle_success = gamma_mle(filtered_transcripts,
+                                        nr_alignments,
+                                        log_conv_factors, 
+                                        filtered_gammas);
+    }
 
 	for (size_t i = 0; i < filtered_gammas.size(); ++i)
 	{
@@ -607,17 +1226,19 @@ bool AbundanceGroup::calculate_gammas(const vector<MateHit>& nr_alignments,
 		}
 	}
 	
-	asm_verbose( "Importance sampling posterior distribution\n");
+	verbose_msg( "Importance sampling posterior distribution\n");
 	
-	bool success = true;
 	size_t N = transcripts.size();
 	
+    AbundanceStatus map_success = NUMERIC_OK;
 	if (final_est_run) // Only on last estimation run.
 	{
-		success = gamma_map(filtered_transcripts,
-							nr_alignments,
-							filtered_gammas,
-							_gamma_covariance);
+		map_success = gamma_map(filtered_transcripts,
+                                            nr_alignments,
+                                            log_conv_factors,
+                                            filtered_gammas,
+                                            _gamma_covariance);
+        
 	}
 	else 
 	{
@@ -630,7 +1251,7 @@ bool AbundanceGroup::calculate_gammas(const vector<MateHit>& nr_alignments,
 		if (isnan(gammas[i]))
 		{
 			verbose_msg( "Warning: isoform abundance is NaN!\n");
-			success = false;
+			map_success = NUMERIC_FAIL;
 		}
 	}
 	
@@ -679,13 +1300,32 @@ bool AbundanceGroup::calculate_gammas(const vector<MateHit>& nr_alignments,
 		}
 	}
 	
+    AbundanceStatus numeric_status = NUMERIC_OK;
+    if (mle_success == NUMERIC_LOW_DATA)
+    {
+        numeric_status = NUMERIC_LOW_DATA;
+    }
+    else if (mle_success == NUMERIC_FAIL)
+    {
+        numeric_status = NUMERIC_FAIL;
+    }
+    else
+    {
+        assert (mle_success == NUMERIC_OK);
+        if (map_success == NUMERIC_FAIL)
+        {
+            numeric_status = NUMERIC_FAIL;
+        }
+        // otherwise, we're cool.
+    }
+    
 	// All scaffolds that go in get abundances, but those that get "filtered"
 	// from the calculation get zeros.
 	//gammas = updated_gammas;
 	for (size_t i = 0; i < _abundances.size(); ++i)
 	{
 		_abundances[i]->gamma(updated_gammas[i]);
-		_abundances[i]->status(success ? NUMERIC_OK : NUMERIC_FAIL);
+		_abundances[i]->status(numeric_status);
 	}
 	_gamma_covariance = updated_gamma_cov;
 	
@@ -838,61 +1478,18 @@ void Mstep (int N, int M, vector<double> & p, vector<vector<double> > const & U)
 	{
 		assert(false);
 	}
-//#ifdef DEBUG
-//	for (j = 0; j < N; ++j) {
-//		cout << p[j] << " ";
-//	}
-//	cout << endl;
-//#endif
-	
 }
  
-//double logLike (int N, 
-//				int M, 
-//				vector<double> & p,
-//				vector<vector<double> > const & cond_prob, 
-//				vector<double> const & u) {
-//	//int i,j;
-//	
-//	double ell = 0;
-//	double Prob_Y;
-//	
-//	double* tmp_prob = (float*)calloc(M, sizeof(float));
-//	
-//	for (int i= 0; i < M; i++) 
-//	{
-//		//tmp_prob[i] = 0;
-//		for (int j= 0; j < N; j++) {
-//			tmp_prob[i] += (float)(cond_prob[i][j] * p[j]);
-//		}
-//	} 
-//	
-//	for (int i= 0; i < M; i++) 
-//	{
-//		float l = tmp_prob[i]; 
-//		tmp_prob[i] = logf(l); 
-//	}
-//	
-//	for (int i= 0; i < M; i++) 
-//	{
-//		if (!isinf(tmp_prob[i]) && !isnan(tmp_prob[i]))
-//		{
-//			ell += tmp_prob[i];
-//		}
-//	}
-//	
-//	free(tmp_prob);
-//	return ell;
-//}
 
 double logLike (int N, 
 				int M, 
 				vector<double> & p,
 				const vector<vector<double> >& cond_prob, 
-				vector<double> const & u) {
+				const vector<double>& u,
+                const vector<double>& log_conv_factors) {
 	int i,j;
 	
-	double ell = 0;
+	double ell = accumulate(log_conv_factors.begin(), log_conv_factors.end(), 0.0);
 	double Prob_Y;
 	for (i= 0; i < M; i++) {
 		Prob_Y = 0;
@@ -906,13 +1503,63 @@ double logLike (int N,
 	return ell;
 }
 
-//#define SEEPROB
+void grad_ascent_step (int N, 
+                       int M, 
+                       vector<double> const & p,
+                       vector<vector<double> >& U,
+                       const vector<vector<double> >& cond_probs,
+                       const vector<double>& u,
+                       vector<double>& newP,
+                       double& epsilon) 
+{
+	// given p, fills U with expected frequencies
+	//int i,j;	
+    
+    vector<double> dLL_dj(N, 0.0);
+    
+    for (size_t i = 0; i < M; ++i)
+    {
+        double denom = 0.0;
+        for (size_t j = 0; j < N; ++j)
+        {
+            denom += p[j] * cond_probs[j][i];
+        }
+        
+        for (size_t j = 0; j < N; ++j)
+        {
+            if (denom > 0)
+            {
+                dLL_dj[j] += u[i] * cond_probs[j][i] / denom;
+            }
+        }
+    }
+    
+    for (size_t j = 0; j < N; ++j)
+    {
+        newP[j] = p[j] + epsilon * dLL_dj[j];
+    }
+    
+    double m = accumulate(newP.begin(), newP.end(), 0.0);
+    if (m > 0)
+    {    
+        for (int j = 0; j < N; ++j) {
+            newP[j] = newP[j] / m;
+        }
+    }
+    else
+    {
+        return;
+    }
+}
 
-double EM (int N, int M, vector<double> & newP, 
-		   const vector<vector<double> >& cond_prob, 
-		   vector<double> const & u) 
+double grad_ascent (int N, int M, vector<double> & newP, 
+                    const vector<vector<double> >& cond_prob, 
+                    vector<double> const & u,
+                    vector<double> const & log_conv_factors,
+                    bool& converged) 
 {
-	double sum = 0;
+    converged = true;
+    double sum = 0;
 	double newEll = 0;
 	vector<double> p(N,0);
 	vector<vector<double> > U(N, vector<double>(M,0));
@@ -921,21 +1568,86 @@ double EM (int N, int M, vector<double> & newP,
 	int j;
 	
 	for (j = 0; j < N; ++j) {
-		p[j] = rand();
+		p[j] = drand48();
 		sum += p[j];
 	}
 	for (j = 0; j < N; ++j) {
 		p[j] = p[j] / sum;
 	}
 	
+    ell = logLike(N, M, p, cond_prob, u, log_conv_factors);
+    
+    double epsilon = 1e-5;
+    
+    static const double ACCURACY = 1e-6; // convergence criteria
+	
+	while (iter <= 2 || iter < max_mle_iterations) 
+    {
+        grad_ascent_step(N, M, p, U, cond_prob, u, newP, epsilon);
+		
+		newEll = logLike(N, M, newP, cond_prob,u, log_conv_factors);
+		
+        double delta = newEll - ell;
+        //fprintf (stderr, "%g\n", delta);
+        if (delta > 0)
+        {
+            //round(newP);
+			p = newP;
+			ell = newEll;
+            if (abs(delta) < ACCURACY)
+            {
+                break;
+            }
+        }
+        else
+        {
+            //verbose_msg("Reducing EPSILON \n");
+            epsilon /= 10;
+        }
+		iter++;
+	}
+	if (iter == max_mle_iterations)
+    {
+		verbose_msg("Warning: ITERMAX reached in abundance estimation, estimation hasn't fully converged\n");
+        converged = false;
+    }
+    verbose_msg("Convergence reached in %d iterations \n", iter);
+	return newEll;
+
+}
+
+double EM (int N, int M, vector<double> & newP, 
+		   const vector<vector<double> >& cond_prob, 
+		   vector<double> const & u,
+           vector<double> const & log_conv_factors,
+           bool& converged) 
+{
+    converged = true;
+	//double sum = 0;
+	double newEll = 0;
+	vector<double> p(N,0);
+	vector<vector<double> > U(N, vector<double>(M,0));
+	double ell = 0; 
+	int iter = 0;
+	int j;
+	
+	for (j = 0; j < N; ++j) {
+		//p[j] = drand48();
+		//sum += p[j];
+        p[j] = 1.0/(double)N;
+	}
+//	for (j = 0; j < N; ++j) {
+//		p[j] = p[j] / sum;
+//	}
+	
 	//#ifdef DEBUG
-	//	for (j = 0; j < N; ++j) {
-	//		cout << p[j] << " ";
-	//	}
-	//	cout << endl;
+//	for (j = 0; j < N; ++j) {
+//		cout << p[j] << " ";
+//	}
+//	cout << endl;
 	//#endif
 
-	static const double ACCURACY = .000001; // convergence for EM
+	static const double ACCURACY = 1e-3; // convergence for EM
 	
 	while (((iter <= 2) || (abs(ell - newEll) > ACCURACY)) && (iter < max_mle_iterations)) {
 		if (iter > 0) {
@@ -947,7 +1659,7 @@ double EM (int N, int M, vector<double> & newP,
 		Estep(N, M, p, U, cond_prob, u); //  fills U
 		Mstep(N, M, newP,U); // fills p
 		
-		newEll = logLike(N, M, newP, cond_prob,u);
+		newEll = logLike(N, M, newP, cond_prob,u, log_conv_factors);
 		
 		//fprintf(stderr, "%d\t%lf\n", iter, newEll);
 		
@@ -957,8 +1669,11 @@ double EM (int N, int M, vector<double> & newP,
 		iter++;
 	}
 	if (iter == max_mle_iterations)
+    {
 		verbose_msg("Warning: ITERMAX reached in abundance estimation, estimation hasn't fully converged\n");
-	//fprintf(stderr, "Convergence reached in %d iterations \n", iter);
+        converged = false;
+    }
+    verbose_msg("Convergence reached in %d iterations \n", iter);
 	return newEll;
 }
 
@@ -1180,7 +1895,8 @@ public:
 	multinormal_generator(const ublas::vector<ValueType>& mean,
 						  const ublas::matrix<ValueType>& chol_cov)
 		:	
-			_engine(time(0)),
+            // FIXME: revert back to seeding on time(NULL)
+			_engine(random_seed),
 			_distribution(),
 			_generator(boost::variate_generator<base_generator_type&, 
 			     distribution_type >(_engine, 
@@ -1270,6 +1986,7 @@ void compute_sample_weights(const ublas::matrix<double>& proposed_cov,
 							const vector<vector<double> >& cond_probs,
 							const vector<ublas::vector<double> >& samples,
 							const vector<double>& u,
+                            const vector<double>& log_conv_factors,
 							double scale,
 							const ublas::vector<double>& MLE,
 							vector<ublas::vector<double> >& weighted_samples,
@@ -1291,10 +2008,11 @@ void compute_sample_weights(const ublas::matrix<double>& proposed_cov,
 
 		
 		double ell = logLike(N,
-								 M,
-								 sample, 
-								 cond_probs, 
-							 u);
+                             M,
+                             sample, 
+                             cond_probs, 
+							 u,
+                             log_conv_factors);
 		
 		ublas::vector<double> diff = (samples[i] - MLE);
 		//cerr << "diff: "<<diff << endl;
@@ -1380,19 +2098,20 @@ void compute_posterior_expectation(const vector<ublas::vector<double> >& weighte
 	}
 }
 
-bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
-			   const vector<MateHit>& nr_alignments,
-			   vector<double>& gamma_map_estimate,
-			   ublas::matrix<double>& gamma_covariance)
+AbundanceStatus gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
+                          const vector<MateHit>& nr_alignments,
+                          const vector<double>& log_conv_factors,
+                          vector<double>& gamma_map_estimate,
+                          ublas::matrix<double>& gamma_covariance)
 {	
-	int N = transcripts.size();	
-	int M = nr_alignments.size();
+	size_t N = transcripts.size();	
+	size_t M = nr_alignments.size();
 	
 	gamma_covariance = ublas::zero_matrix<double>(N);
 	
-	if (N == 1 || M == 0.0)
+	if (N == 1 || M == 0)
 	{
-		return true;
+		return NUMERIC_OK;
 	}
 
 	typedef ublas::matrix<double> matrix_type;
@@ -1410,7 +2129,7 @@ bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
 				   fisher);
 	
 	ublas::matrix<double> epsilon = ublas::zero_matrix<double>(N,N);
-	for (int i = 0; i < N; ++i)
+	for (size_t i = 0; i < N; ++i)
 	{
 		epsilon(i,i) = 1e-6;
 	}
@@ -1427,7 +2146,7 @@ bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
 	if (ch != 0.0)
 	{
 		verbose_msg("Warning: Fisher matrix is not positive definite (bad element: %lg)\n", ch);
-		return false;
+		return NUMERIC_FAIL;
 	}
 	
 	//cerr << "FISHER" << fisher << endl << endl;
@@ -1451,11 +2170,11 @@ bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
 	if (ch != 0.0 || !invertible)
 	{
 		verbose_msg("Warning: Inverse fisher matrix is not positive definite (bad element: %lg)\n", ch);
-		return false;
+		return NUMERIC_FAIL;
 	}
 	
 	ublas::vector<double> MLE(N);
-	for (int i = 0; i < N; ++i)
+	for (size_t i = 0; i < N; ++i)
 	{
 		MLE(i) = gamma_map_estimate[i];
 	}
@@ -1476,10 +2195,10 @@ bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
 	if (ch != 0.0)
 	{
 		verbose_msg("Warning: Covariance matrix is not positive definite (bad element: %lg)\n", ch);
-		return false;
+		return NUMERIC_FAIL;
 	}
 	
-	chol_invert_matrix(covariance_chol, inv_cov);
+	invertible = chol_invert_matrix(covariance_chol, inv_cov);
 	
 	//cerr << "COV" << endl << covariance << endl;
 	//cerr << "COV^-1" << inv_cov << endl;
@@ -1488,21 +2207,24 @@ bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
 	multinormal_generator<double> generator(MLE, covariance_chol);
 	
 	vector<ublas::vector<double> > samples;
-	//int num_samples = 1000;
-	for (int i = 0; i < num_importance_samples; ++i)
+	//int num_samples = std::min((int)N * 1000, num_importance_samples);
+    int num_samples = num_importance_samples;
+	for (int i = 0; i < num_samples; ++i)
 	{
 		ublas::vector<double> r = generator.next_rand();
 		ublas::vector<double> scaled_sample = r;
 		
-		for (int j = 0; j < N; ++j) {
-			if (scaled_sample(j) < 0)
-				scaled_sample(j) = 1e-10;
+		for (size_t j = 0; j < N; ++j) {
+//			if (scaled_sample(j) < 0)
+//				scaled_sample(j) = 1e-10;
+            if (scaled_sample(j) < 0)
+                scaled_sample(j) = -scaled_sample(j);
 		}
 		
 		double m = sum(scaled_sample);
 		if (m && !isnan(m))
 		{
-			for (int j = 0; j < N; ++j) {
+			for (size_t j = 0; j < N; ++j) {
 				scaled_sample(j) = scaled_sample(j) / m;
 			}
 			
@@ -1530,7 +2252,7 @@ bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
 	if (samples.size() < 100)
 	{
 		verbose_msg("Warning: not-enough samples for MAP re-estimation\n");
-		return false;
+		return NUMERIC_FAIL;
 	}
 	
 	double det = determinant(covariance_chol);
@@ -1542,9 +2264,9 @@ bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
 	//assert (det);
 	if (s == 0.0)
 	{
-		fprintf(stderr, "Error: sqrt(det(cov)) == 0, %lf after rounding. \n", det);
+		verbose_msg("Error: sqrt(det(cov)) == 0, %lf after rounding. \n", det);
 		//cerr << covariance << endl;
-		return false;
+		return NUMERIC_FAIL;
 	}
 	assert (s);
 	assert (denom);
@@ -1554,7 +2276,7 @@ bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
 	if (!invertible)
 	{
 		verbose_msg("Warning: covariance matrix is not invertible, probability interval is not available\n");
-		return false;
+		return NUMERIC_FAIL;
 	}
 	
 	long double log_total_weight = 0.0;
@@ -1564,7 +2286,7 @@ bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
 	vector<ublas::vector<double> > weighted_samples;
 	
 	vector<vector<double> > cond_probs(N, vector<double>());
-	for(int j = 0; j < N; ++j)
+	for(size_t j = 0; j < N; ++j)
 	{
 		cond_probs[j]= *(transcripts[j]->cond_probs());
 	}
@@ -1573,6 +2295,7 @@ bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
 						   cond_probs,
 						   samples,
 						   u,
+                           log_conv_factors,
 						   denom,
 						   MLE,
 						   weighted_samples,
@@ -1586,7 +2309,7 @@ bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
 	if (log_total_weight == 0 || sample_weights.size() < 100)
 	{
 		verbose_msg("Warning: restimation failed, importance samples have zero weight.\n\tResorting to MLE and observed Fisher\n");
-		return false;
+		return NUMERIC_FAIL;
 	}
 	
 	ublas::vector<long double> expectation(N);
@@ -1601,7 +2324,7 @@ bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
 		if (isinf(expectation(e)) || isnan(expectation(e)))
 		{
 			verbose_msg("Warning: isoform abundance is NaN, restimation failed.\n\tResorting to MLE and observed Fisher.");
-			return false;
+			return NUMERIC_FAIL;
 		}
 	}
 	
@@ -1610,7 +2333,7 @@ bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
 	//expectation /= total_weight;
 	//cerr << expectation << endl;
 	
-	for (int j = 0; j < N; ++j) 
+	for (size_t j = 0; j < N; ++j) 
 	{
 		if (expectation(j) < 0)
 			expectation(j) = 0;
@@ -1621,10 +2344,10 @@ bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
 	if (m == 0 || isinf(m) || isnan(m))
 	{
 		verbose_msg("Warning: restimation failed, could not renormalize MAP estimate\n\tResorting to MLE and observed Fisher.");
-		return false;
+		return NUMERIC_FAIL;
 	}
 	
-	for (int j = 0; j < N; ++j) {
+	for (size_t j = 0; j < N; ++j) {
 		expectation(j) = expectation(j) / m;
 	}
 	
@@ -1656,9 +2379,9 @@ bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
 	ublas::matrix<double> revised_cov = ublas::zero_matrix<double>(N,N);
 	
 	// initialize the revised covariance with the values from the first sample
-	for (int k = 0; k < N; ++k)
+	for (size_t k = 0; k < N; ++k)
 	{
-		for (int i = 0; i < N; ++i)
+		for (size_t i = 0; i < N; ++i)
 		{
 			double log_sample_weight = sample_weights[0].second;
 			double x = sample_expectation_diffs[0](k);
@@ -1672,9 +2395,9 @@ bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
 	
 	// accumulate the contributions from the other samples (doing one cell of 
 	// covariance matrix per outer (i x j) loop iteration.
-	for (int k = 0; k < N; ++k)
+	for (size_t k = 0; k < N; ++k)
 	{
-		for (int i = 0; i < N; ++i)
+		for (size_t i = 0; i < N; ++i)
 		{
 			for (size_t j = 1; j < sample_expectation_diffs.size(); ++j)
 			{
@@ -1690,28 +2413,64 @@ bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
 	//cerr << revised_cov << endl;
 	gamma_covariance = revised_cov;
 	
-	return true;
+	return NUMERIC_OK;
+}
+
+template<class M, class PM>
+bool is_identifiable(M &m, PM &pm)
+{
+    using namespace ublas;
+    typedef M matrix_type;
+    typedef typename M::size_type size_type;
+    typedef typename M::value_type value_type;
+
+    int singular = 0;
+    size_type size1 = m.size1 ();
+    size_type size2 = m.size2 ();
+    size_type size = (std::min) (size1, size2);
+    for (size_type i = 0; i < size; ++ i) {
+        matrix_column<M> mci (column (m, i));
+        matrix_row<M> mri (row (m, i));
+        size_type i_norm_inf = i + index_norm_inf (project (mci, range (i, size1)));
+        if (m (i_norm_inf, i) != value_type/*zero*/()) {
+            if (i_norm_inf != i) {
+                pm (i) = i_norm_inf;
+                row (m, i_norm_inf).swap (mri);
+            } else {
+                //BOOST_UBLAS_CHECK (pm (i) == i_norm_inf, external_logic ());
+            }
+            project (mci, range (i + 1, size1)) *= value_type (1) / m (i, i);
+        } else if (singular == 0) {
+            singular = i + 1;
+        }
+        project (m, range (i + 1, size1), range (i + 1, size2)).minus_assign (outer_prod (project (mci, range (i + 1, size1)),
+                                                                              project (mri, range (i + 1, size2))));
+    }
+    return singular == 0;
 }
 
-void gamma_mle(const vector<shared_ptr<Abundance> >& transcripts,
-			   const vector<MateHit>& nr_alignments,
-			   vector<double>& gammas)
+AbundanceStatus gamma_mle(const vector<shared_ptr<Abundance> >& transcripts,
+                          const vector<MateHit>& nr_alignments,
+                          const vector<double>& log_conv_factors,
+                          vector<double>& gammas)
 {
 	gammas.clear();
 	if (transcripts.empty())
-		return;
+		return NUMERIC_OK;
 	
 	//long double bundle_mass_fraction = bundle_mass / (long double) map_mass;
 	if (transcripts.size() == 1)
 	{
 		gammas.push_back(1.0);
-		return;
+		return NUMERIC_OK;
 	}
 	
-	int M = nr_alignments.size();
-	int N = transcripts.size();
+	size_t M = nr_alignments.size();
+	size_t N = transcripts.size();
 
-	
+    bool converged = true;
+    bool identifiable = true;
+    
 	if (M > 0)
 	{
 		
@@ -1730,15 +2489,65 @@ void gamma_mle(const vector<shared_ptr<Abundance> >& transcripts,
 		{
 			cond_probs[j] = *(transcripts[j]->cond_probs());
 		}
+        
+        ublas::matrix<double> compat = ublas::zero_matrix<double>(M,N);
+        
+        for (size_t j = 0; j < N; ++j)
+        {
+            for (size_t i = 0; i < M; ++i)
+            {
+                if (cond_probs[j][i])
+                    compat(i,j) = cond_probs[j][i];
+            }
+        }
+        
+        vector<size_t> transcripts_with_frags;
+        for (size_t j = 0; j < N; ++j)
+        {
+            bool has_fragment = false;
+            for (size_t i = 0; i < M; ++i)
+            {
+                if (compat(i,j))
+                {
+                    has_fragment = true;
+                    break;
+                }
+            }
+            if (has_fragment)
+                transcripts_with_frags.push_back(j);
+        }
+        ublas::matrix<double> reduced_compat = ublas::zero_matrix<double>(M,transcripts_with_frags.size());
+        for (size_t j = 0; j < transcripts_with_frags.size(); ++j)
+        {
+            column(reduced_compat, j) = column(compat, transcripts_with_frags[j]);
+        }
+        
+        
+        typedef ublas::permutation_matrix<std::size_t> pmatrix;
+        
+        // create a permutation matrix for the LU-factorization
+        pmatrix pm(reduced_compat.size1());
+        
+        // cerr << compat.size2() <<endl;
+        // perform LU-factorization
+        identifiable = is_identifiable<ublas::matrix<double>,pmatrix>(reduced_compat,pm);
+
+        
 		vector<double> u(M);
 		for (size_t i = 0; i < M; ++i)
 		{
 			u[i] = nr_alignments[i].collapse_mass();
 		}
-		
-		
-		logL = EM(N, M, prob, cond_probs, u);
-
+        		
+        if (use_em)
+        {
+            logL = EM(N, M, prob, cond_probs, u, log_conv_factors, converged);
+        }
+		else
+        {
+            logL = grad_ascent(N, M, prob, cond_probs, u, log_conv_factors, converged);
+        }
+        
 		gammas = prob;
 		
 		for (size_t i = 0; i < gammas.size(); ++i)
@@ -1750,6 +2559,41 @@ void gamma_mle(const vector<shared_ptr<Abundance> >& transcripts,
 	{
 		gammas = vector<double>(N, 0.0);
 	}
+    
+    double round_err = 0.0;
+    double num_good = 0;
+    foreach (double& g, gammas)
+    {
+        if (g < min_isoform_fraction)
+        {
+            round_err += g;
+            g = 0.0;
+        }
+        else
+        {
+            num_good += 1;
+        }
+    }
+    foreach (double& g, gammas)
+    {
+        if (g != 0)
+        {
+            g += (round_err/num_good);
+        }
+    }
+    
+    if (converged && identifiable)
+        return NUMERIC_OK;
+    else
+    {
+        if (!identifiable)
+            return NUMERIC_LOW_DATA;
+            //return NUMERIC_OK;
+        else
+            return NUMERIC_FAIL;
+    }
+    
+    return NUMERIC_OK;
 }
 
 void calc_isoform_fpkm_conf_intervals(double FPKM,
@@ -1787,6 +2631,7 @@ double compute_doc(int bundle_origin,
 		++itr)
 	{
 		hits.push_back(Scaffold(**itr));
+        hits.back().fpkm((**itr).mass());
 	}
 	
 	for (size_t i = 0; i < hits.size(); ++i)
@@ -1815,13 +2660,13 @@ double compute_doc(int bundle_origin,
 			{
 				for (int K = op.g_left(); K < op.g_right(); ++K)
 				{
-					depth_of_coverage[K - bundle_origin]++;
+					depth_of_coverage[K - bundle_origin] += hits[i].fpkm();
 				}
 			}
 			else if (op.opcode == CUFF_INTRON)
 			{
 				pair<map<pair<int,int>,int>::iterator, bool> is = intron_depth_of_coverage.insert(make_pair(make_pair(op.g_left(), op.g_right()), 0));
-				is.first->second++;
+				is.first->second += hits[i].fpkm();
 			}
 		}
 	}
@@ -1971,7 +2816,7 @@ double get_intron_doc(const Scaffold& s,
 					 zi != intron_depth_of_coverage.end();
 					 ++zi)
 				{
-					asm_verbose( "intron: [%d-%d], %d\n", zi->first.first, zi->first.second, zi->second);
+					verbose_msg( "intron: [%d-%d], %d\n", zi->first.first, zi->first.second, zi->second);
 				}
 			}
 
diff --git a/src/abundances.h b/src/abundances.h
index 10c289d..b3fa1a5 100644
--- a/src/abundances.h
+++ b/src/abundances.h
@@ -24,7 +24,6 @@
 
 namespace ublas = boost::numeric::ublas;
 
-
 struct ConfidenceInterval
 {
 	ConfidenceInterval(double Low = 0.0, double High = 0.0) 
@@ -33,7 +32,7 @@ struct ConfidenceInterval
 	double high;
 };
 
-enum AbundanceStatus { NUMERIC_OK, NUMERIC_FAIL };
+enum AbundanceStatus { NUMERIC_OK, NUMERIC_FAIL, NUMERIC_LOW_DATA };
 
 class Abundance
 {
@@ -72,6 +71,9 @@ public:
     virtual double          mass_fraction() const = 0;
 	virtual void            mass_fraction(double mf) = 0;
     
+    virtual double          mass_variance() const = 0;
+	virtual void            mass_variance(double mv) = 0;
+    
 	virtual double			effective_length() const= 0;
 	virtual void			effective_length(double el) = 0;
 	
@@ -111,7 +113,8 @@ public:
 		_num_fragments(0),
 		_eff_len(0),
 		_cond_probs(NULL),
-        _sample_mass_fraction(0.0) {}
+        _sample_mass_fraction(0.0),
+        _sample_mass_variance(0.0){}
 	
 	TranscriptAbundance(const TranscriptAbundance& other)
 	{
@@ -124,6 +127,7 @@ public:
 		_eff_len = other._eff_len;
 		_cond_probs = other._cond_probs;
         _sample_mass_fraction = other._sample_mass_fraction;
+        _sample_mass_variance = other._sample_mass_variance;
 	}
 	
 	~TranscriptAbundance()
@@ -162,6 +166,9 @@ public:
 	double mass_fraction() const			{ return _sample_mass_fraction; }
 	void mass_fraction(double mf)			{ _sample_mass_fraction = mf; }
 	
+    double mass_variance() const			{ return _sample_mass_variance; }
+	void mass_variance(double mv)			{ _sample_mass_variance = mv; }
+	
 	void transfrag(shared_ptr<Scaffold> tf)		{ _transfrag = tf; }
 	shared_ptr<Scaffold> transfrag() const		{ return _transfrag; }
 	
@@ -265,12 +272,13 @@ private:
 	string _ref_tag;
 	
     long double _sample_mass_fraction;
+    long double _sample_mass_variance;
 };
 
 class AbundanceGroup : public Abundance
 {
 public:
-	AbundanceGroup() : _kappa(1.0), _FPKM_variance(0.0) {}
+	AbundanceGroup() : _kappa(1.0), _FPKM_variance(0.0), _max_mass_variance(0.0) {}
 	
 	AbundanceGroup(const AbundanceGroup& other) 
 	{
@@ -281,6 +289,7 @@ public:
 		_kappa_covariance = other._kappa_covariance;
 		_FPKM_variance = other._FPKM_variance;
 		_description = other._description;
+        _max_mass_variance = other._max_mass_variance;
 	}
 	
 	AbundanceGroup(const vector<shared_ptr<Abundance> >& abundances) : 
@@ -288,12 +297,16 @@ public:
 		_gamma_covariance(ublas::zero_matrix<double>(abundances.size(), abundances.size())), 
 		_kappa_covariance(ublas::zero_matrix<double>(abundances.size(), abundances.size())),
 		_kappa(1.0),
-		_FPKM_variance(0.0){}
+		_FPKM_variance(0.0), 
+        _max_mass_variance(0.0){}
 	AbundanceGroup(const vector<shared_ptr<Abundance> >& abundances,
-				   const ublas::matrix<double>& gamma_covariance) :
+				   const ublas::matrix<double>& gamma_covariance,
+                   const long double max_mass_variance) :
 		_abundances(abundances), 
-		_gamma_covariance(gamma_covariance)
+		_gamma_covariance(gamma_covariance),
+        _max_mass_variance(max_mass_variance)
 	{
+        
 		calculate_conf_intervals();
 		calculate_kappas();
 	}
@@ -321,6 +334,9 @@ public:
     
     double mass_fraction() const;
 	void mass_fraction(double mf)			{  }
+    
+    double mass_variance() const;
+	void mass_variance(double mf)	{  }
 	
 	set<string> gene_id() const;	
 	set<string> gene_name() const;
@@ -357,17 +373,24 @@ public:
 	
 	void calculate_abundance(const vector<MateHit>& alignments);
 	
+    void max_mass_variance(double mmv) { _max_mass_variance = mmv; }
+    double max_mass_variance() const { return _max_mass_variance; }
+    
 private:
 	
 	void FPKM_conf(const ConfidenceInterval& cf)  { _FPKM_conf = cf; }
 	
 	bool calculate_gammas(const vector<MateHit>& nr_alignments, 
+                          const vector<double>& log_conv_factors,
 						  const vector<shared_ptr<Abundance> >& transcripts,
 						  const vector<shared_ptr<Abundance> >& mapped_transcripts);
 	void calculate_FPKM_variance();
 	void calculate_conf_intervals();
-	void calculate_counts(const vector<MateHit>& nr_alignments, const vector<shared_ptr<Abundance> >& transcripts);
+	void calculate_counts(const vector<MateHit>& nr_alignments,
+                          const vector<shared_ptr<Abundance> >& transcripts);
 	void calculate_kappas();
+	void update_multi_reads(const vector<MateHit>& alignments, vector<shared_ptr<Abundance> > transcripts);
+
     
 	void compute_cond_probs_and_effective_lengths(const vector<MateHit>& alignments, 
 												  vector<shared_ptr<Abundance> >& transcripts,
@@ -382,7 +405,7 @@ private:
 	double _kappa;
 	double _FPKM_variance;
 	string _description;
-
+    double _max_mass_variance;  // upper bound on the count variance that could come from this group.
 };
 
 void compute_compatibilities(vector<shared_ptr<Abundance> >& transcripts,
@@ -392,14 +415,16 @@ void compute_compatibilities(vector<shared_ptr<Abundance> >& transcripts,
 void get_alignments_from_scaffolds(const vector<shared_ptr<Abundance> >& abundances,
 								   vector<MateHit>& alignments);
 
-bool gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
-			   const vector<MateHit>& nr_alignments,
-			   vector<double>& gamma_map_estimate,
-			   ublas::matrix<double>& gamma_covariance);
+AbundanceStatus gamma_map(const vector<shared_ptr<Abundance> >& transcripts,
+                          const vector<MateHit>& nr_alignments,
+                          const vector<double>& log_conv_factors,
+                          vector<double>& gamma_map_estimate,
+                          ublas::matrix<double>& gamma_covariance);
 
-void gamma_mle(const vector<shared_ptr<Abundance> >& transcripts,
-			   const vector<MateHit>& nr_alignments,
-			   vector<double>& gammas);
+AbundanceStatus gamma_mle(const vector<shared_ptr<Abundance> >& transcripts,
+                          const vector<MateHit>& nr_alignments,
+                          const vector<double>& log_conv_factors,
+                          vector<double>& gammas);
 
 double compute_doc(int bundle_origin, 
 				   const vector<Scaffold>& scaffolds,
diff --git a/src/assemble.cpp b/src/assemble.cpp
index 40af772..91a693f 100644
--- a/src/assemble.cpp
+++ b/src/assemble.cpp
@@ -217,7 +217,7 @@ void add_weights_to_reachability_bp_graph(ReachGraph& bp,
 	{
 		for (size_t j = 0; j < hits.size(); ++j)
 		{
-			if (scaffolds[i].contains(hits[j]))
+			if (!hits[j].is_ref() && scaffolds[i].contains(hits[j]))
 			{
 				if (Scaffold::compatible(scaffolds[i],hits[j]))
 				{
@@ -318,10 +318,8 @@ void holdout_transitivity_hazards(vector<Scaffold>& hits,
 			if (overlap_in_genome(introns[i].first, introns[i].second,
 								  introns[j].first, introns[j].second))
 			{
-				{
-					evil_introns.push_back(introns[i]);
-					evil_introns.push_back(introns[j]);
-				}
+				evil_introns.push_back(introns[i]);
+				evil_introns.push_back(introns[j]);
 			}
 		}
 	}
@@ -365,7 +363,7 @@ void holdout_transitivity_hazards(vector<Scaffold>& hits,
 		}
 	}
 	
-	asm_verbose( "%s\tHeld out %lu scaffolds as transitivity hazards\n", bundle_label->c_str(), hazards.size());
+	verbose_msg( "%s\tHeld out %lu scaffolds as transitivity hazards\n", bundle_label->c_str(), hazards.size());
 	
 	hits = filtered_hits;
 }
@@ -378,25 +376,25 @@ bool make_scaffolds(int bundle_left,
 	if (hits.empty())
 		return true;
 
-	int intron_hits = 0;
+	bool intron_hits = false;
 	for (size_t i = 0; i < hits.size(); ++i)
 	{
 		if (hits[i].has_intron())
 		{
-			intron_hits++;
+			intron_hits = true;
+			break;
 		}
 	}
 	
 	if (!intron_hits)
 	{
-
-		asm_verbose( "%s\tNo introns in bundle, collapsing all hits to single transcript\n", bundle_label->c_str());
+		verbose_msg( "%s\tNo introns in bundle, collapsing all hits to single transcript\n", bundle_label->c_str());
 		scaffolds.push_back(Scaffold(hits));
 		fill_gaps(scaffolds, 2 * olap_radius);
 	}
 	else
 	{
-		asm_verbose( "%s\tBundle has %d spliced reads\n", bundle_label->c_str(), intron_hits);
+		verbose_msg( "%s\tBundle has spliced reads\n", bundle_label->c_str());
 
 		vector<Scaffold> hazards;
 		holdout_transitivity_hazards(hits, hazards);
@@ -414,6 +412,7 @@ bool make_scaffolds(int bundle_left,
 		vector<Scaffold> orig_hits = hits;
 		
         hits.insert(hits.end(), split_hazards.begin(), split_hazards.end());
+		
 
         compress_fragments(hits);
 
@@ -442,14 +441,14 @@ bool make_scaffolds(int bundle_left,
 			
 			if (hits.empty())
 				return true;
-			asm_verbose( "%s\tCalculating scaffold densities\n", bundle_label->c_str());
+			verbose_msg( "%s\tCalculating scaffold densities\n", bundle_label->c_str());
 			vector<double> scaff_doc;
 			record_doc_for_scaffolds(bundle_left, 
 									 hits, 
 									 depth_of_coverage, 
 									 intron_depth_of_coverage,
 									 scaff_doc);
-			asm_verbose( "%s\tCreating compatibility graph\n", bundle_label->c_str());
+			verbose_msg( "%s\tCreating compatibility graph\n", bundle_label->c_str());
 			
 			if (!create_overlap_dag(hits, bundle_dag))
 			{			
@@ -472,7 +471,7 @@ bool make_scaffolds(int bundle_left,
             
 			ReachGraph bp;
 			
-			asm_verbose( "%s\tConstructing reachability graph\n", bundle_label->c_str());
+			verbose_msg( "%s\tConstructing reachability graph\n", bundle_label->c_str());
 			
 			vector<ReachGraph::BNode> b_to_a;
 			adjacency_list<> TC;
@@ -488,7 +487,7 @@ bool make_scaffolds(int bundle_left,
 			ReachGraph::UEdgeMap<long long> cov_weights(bp);
 			add_weights_to_reachability_bp_graph(bp, hits_for_node, orig_hits, hits, cov_weights);				
 
-            asm_verbose( "%s\tPerforming weighted matching\n", bundle_label->c_str());
+            verbose_msg( "%s\tPerforming weighted matching\n", bundle_label->c_str());
 
             typedef lemon::MinCostMaxBipartiteMatching<ReachGraph,ReachGraph::UEdgeMap<long long> > Matcher;
             Matcher matcher(bp, cov_weights);
@@ -497,17 +496,17 @@ bool make_scaffolds(int bundle_left,
             vector<vector<DAGNode> > chains;
             make_chains_from_matching<Matcher>(bp, matcher, chains);
 			
-			asm_verbose( "%s\tFound %d distinct chains\n", bundle_label->c_str(), (int)chains.size());
+			verbose_msg( "%s\tFound %d distinct chains\n", bundle_label->c_str(), (int)chains.size());
 			
 			vector<vector<DAGNode> > paths;
 			extend_chains_to_paths(bundle_dag, chains, TC, source, sink, paths);
 
-			asm_verbose( "%s\tCreating scaffolds for %d paths\n", bundle_label->c_str(), (int)paths.size());
+			verbose_msg( "%s\tCreating scaffolds for %d paths\n", bundle_label->c_str(), (int)paths.size());
 			
 			vector<Scaffold> new_scaffs;
 			make_scaffolds_from_paths(bundle_dag, paths, new_scaffs);
 			
-			asm_verbose( "%s\tCollapsing scaffolds\n", bundle_label->c_str());
+			verbose_msg( "%s\tCollapsing scaffolds\n", bundle_label->c_str());
            
 			collapse_contained_transfrags(new_scaffs);
 			hits = new_scaffs;
@@ -518,7 +517,7 @@ bool make_scaffolds(int bundle_left,
 		// One last collapse attempt...
 		vector<Scaffold> new_scaffs = scaffolds;
 		
-		asm_verbose( "%s\tPerforming final collapse round\n", bundle_label->c_str());
+		verbose_msg( "%s\tPerforming final collapse round\n", bundle_label->c_str());
 		
 		fill_gaps(new_scaffs, 2 * olap_radius);
         
@@ -534,7 +533,7 @@ bool make_scaffolds(int bundle_left,
             completes.insert(completes.end(), c.begin(), c.end()); 
         } 
         
-        asm_verbose( "Extracted %lu contiguous transfrags from %lu scaffolds\n", completes.size(), scaffolds.size());
+        verbose_msg( "Extracted %lu contiguous transfrags from %lu scaffolds\n", completes.size(), scaffolds.size());
         
         new_scaffs = completes;
         sort(new_scaffs.begin(), new_scaffs.end(), scaff_lt);
@@ -567,4 +566,3 @@ bool make_scaffolds(int bundle_left,
 	
 	return true;
 }
-
diff --git a/src/assemble.h b/src/assemble.h
index 5f8997c..a3b4852 100644
--- a/src/assemble.h
+++ b/src/assemble.h
@@ -17,16 +17,11 @@
 #include <vector>
 #include <map>
 
-//#include <lemon/list_graph.h>
 
-//#include <lemon/concepts/bpugraph.h>
-#include <boost/graph/adjacency_list.hpp>
-
-#include "hits.h"
 #include "bundles.h"
 #include "scaffolds.h"
-#include "biascorrection.h"
 
+class BiasLearner;
 
 bool assemble_hits(BundleFactory& bundle_factory, BiasLearner* bl_ptr);
 
diff --git a/src/biascorrection.cpp b/src/biascorrection.cpp
index 9c349a0..62e85b9 100644
--- a/src/biascorrection.cpp
+++ b/src/biascorrection.cpp
@@ -8,12 +8,16 @@
  */
 
 #include "biascorrection.h"
+#include "scaffolds.h"
 #include "abundances.h"
 #include "progressbar.h"
+#include "bundles.h"
 
 #include <iostream>
 #include <fstream>
 
+using namespace std;
+
 void output_vector(vector<double>& v, char* fname)
 {
 	ofstream myfile1;
@@ -97,7 +101,7 @@ void learn_bias(BundleFactory& bundle_factory, BiasLearner& bl, bool progress_ba
 
 	ProgressBar p_bar;
 	if (progress_bar)
-		ProgressBar p_bar("Learning bias parameters.", bundle_factory.num_bundles());
+		p_bar = ProgressBar("Learning bias parameters.", bundle_factory.read_group_properties()->total_map_mass());
 
 	while(true)
 	{
@@ -114,7 +118,7 @@ void learn_bias(BundleFactory& bundle_factory, BiasLearner& bl, bool progress_ba
 		char bundle_label_buf[2048];
 		sprintf(bundle_label_buf, "%s:%d-%d", rt.get_name(bundle.ref_id()),	bundle.left(), bundle.right());
 		if (progress_bar)
-			p_bar.update(bundle_label_buf, 1);
+			p_bar.update(bundle_label_buf, bundle.raw_mass());
 		
 		if (bundle.non_redundant_hits().size()==0 || bundle.ref_scaffolds().size() != 1)
 		{
@@ -131,103 +135,33 @@ void learn_bias(BundleFactory& bundle_factory, BiasLearner& bl, bool progress_ba
 		p_bar.complete();
 		
 	bl.normalizeParameters();
-#if ADAM_MODE
-	bl.output();
-#endif
+    
+    if (output_bias_params)
+        bl.output();
 }
 
-// void process_bundle(HitBundle& bundle, BiasLearner& bl)
-// {
-// 	const vector<shared_ptr<Scaffold> >& transcripts = bundle.ref_scaffolds();
-// 	const vector<MateHit>& nr_alignments = bundle.non_redundant_hits();
-// 	
-// 	int M = nr_alignments.size();
-// 	int N = transcripts.size();
-// 	
-// 	vector<list<int> > compatibilities(M,list<int>());
-// 	get_compatibility_list(transcripts, nr_alignments, compatibilities);	
-// 	
-// 	vector<vector<double> > startHists(N, vector<double>());
-// 	vector<vector<double> > endHists(N, vector<double>());
-// 	vector<double> useable_mass (N, 0.0);
-// 	
-// 	for (int j = 0; j < N; ++j)
-// 	{
-// 		int size = transcripts[j]->length();
-// 		startHists[j].resize(size+1); // +1 catches overhangs
-// 		endHists[j].resize(size+1);
-// 	}
-// 	
-// 	for (int i = 0; i < M; ++i)
-// 	{
-// 		const MateHit& hit = nr_alignments[i];
-// 		if (!hit.left_alignment() && !hit.right_alignment())
-// 			continue;
-// 
-// 		double num_hits = nr_alignments[i].collapse_mass();
-// 		long double locus_fpkm = 0;
-// 		
-// 		for (list<int>::iterator it=compatibilities[i].begin(); it!=compatibilities[i].end(); ++it)
-// 		{
-// 			locus_fpkm += transcripts[*it]->fpkm(); 
-// 		}
-// 		
-// 		if (locus_fpkm==0)
-// 			continue;
-// 		
-// 		for (list<int>::iterator it=compatibilities[i].begin(); it!=compatibilities[i].end(); ++it)
-// 		{	
-// 			const Scaffold& transcript = *transcripts[*it];
-// 			assert(transcript.strand()!=CUFF_STRAND_UNKNOWN); //Filtered in compatibility list
-// 			
-// 			int start;
-// 			int end;
-// 			int frag_len;
-// 			
-// 			double mass_fraction = num_hits*transcript.fpkm()/locus_fpkm;
-// 			
-// 			transcript.map_frag(hit, start, end, frag_len);
-// 			
-// 			if (start != transcript.length() || end != transcript.length())
-// 				useable_mass[*it] += mass_fraction;
-// 			
-// 			startHists[*it][start] += mass_fraction;
-// 			endHists[*it][end] += mass_fraction;
-// 		}
-// 	}
-//  	for (int j = 0; j < N; ++j)
-//  	{
-//  		if (transcripts[j]->strand()!=CUFF_STRAND_UNKNOWN && 
-// 			transcripts[j]->seq()!="" &&  
-// //			useable_mass[j]/transcripts[j]->length() >= 0.1 &&
-// 			transcripts[j]->fpkm() >= 1)
-//  			bl.processTranscript(startHists[j], endHists[j], *transcripts[j]);
-//  	}
-// }
-
 const int BiasLearner::pow4[] = {1,4,16,64};
 const int BiasLearner::siteSpec[] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
 const int BiasLearner::vlmmSpec[] = {1,1,1,1,1,2,2,2,3,3,3,3,3,3,3,3,2,2,2,1,1}; //Length of connections at each position in the window
 const int BiasLearner::MAX_SLICE = 3; // Maximum connection length
 const int BiasLearner::CENTER = 8; //Index in paramTypes[] of first element in read
-const int BiasLearner::_M = 21; //Number of positions spanned by window
-const int BiasLearner::_N = 64; //Length of maximum connection in VLMM
+const int BiasLearner::_m = 21; //Number of positions spanned by window
+const int BiasLearner::_n = 64; //Length of maximum connection in VLMM
 const int BiasLearner::lengthBins[] = {791,1265,1707,2433}; //Quantiles derived from human mRNA length distribution in UCSC genome browser
 const double BiasLearner::positionBins[] = {.02,.04,.06,.08,.10,.15,.2,.3,.4,.5,.6,.7,.8,.85,.9,.92,.94,.96,.98,1};
-//const double BiasLearner::positionBins[] = {0.01,0.02,0.03,0.04,0.05,0.06,0.07,0.08,0.09,0.10,0.11,0.12,0.13,0.14,0.15,0.16,0.17,0.18,0.19,0.20,0.21,0.22,0.23,0.24,0.25,0.26,0.27,0.28,0.29,0.30,0.31,0.32,0.33,0.34,0.35,0.36,0.37,0.38,0.39,0.40,0.41,0.42,0.43,0.44,0.45,0.46,0.47,0.48,0.49,0.50,0.51,0.52,0.53,0.54,0.55,0.56,0.57,0.58,0.59,0.60,0.61,0.62,0.63,0.64,0.65,0.66,0.67,0.68,0.69,0.70,0.71,0.72,0.73,0.74,0.75,0.76,0.77,0.78,0.79,0.80,0.81,0.82,0.83,0.84,0.85,0.86,0.87,0.88,0.89,0.90,0.91,0.92,0.93,0.94,0.95,0.96,0.97,0.98,0.99,1};
 
 BiasLearner::BiasLearner(shared_ptr<EmpDist const> frag_len_dist)
 {
 	paramTypes = vlmmSpec;
-	if (bias_mode=="site")
+	if (bias_mode==SITE || bias_mode==POS_SITE)
 	{
 		paramTypes = siteSpec;
 	}
 	_frag_len_dist = frag_len_dist;
-	_startSeqParams = ublas::zero_matrix<long double>(_M,_N);
-	_startSeqExp = ublas::zero_matrix<long double>(_M,_N);
-	_endSeqParams = ublas::zero_matrix<long double>(_M,_N);
-	_endSeqExp = ublas::zero_matrix<long double>(_M,_N);
+	_startSeqParams = ublas::zero_matrix<long double>(_m,_n);
+	_startSeqExp = ublas::zero_matrix<long double>(_m,_n);
+	_endSeqParams = ublas::zero_matrix<long double>(_m,_n);
+	_endSeqExp = ublas::zero_matrix<long double>(_m,_n);
 	_startPosParams = ublas::zero_matrix<long double>(20,5);
 	_startPosExp = ublas::zero_matrix<long double>(20,5);
 	_endPosParams = ublas::zero_matrix<long double>(20,5);
@@ -324,7 +258,7 @@ void BiasLearner::processTranscript(const std::vector<double>& startHist, const
 	{
 		
 		//Position Bias
-		if (i > startBinCutoff && currStartBin < _startPosParams.size1()-1)
+		if (i > startBinCutoff && currStartBin < (int)_startPosParams.size1()-1)
 			startBinCutoff=positionBins[++currStartBin]*(seqLen - min_frag_len);
 		if (i - min_frag_len > endBinCutoff)
 			endBinCutoff = positionBins[++currEndBin]*(seqLen - min_frag_len);
@@ -335,14 +269,14 @@ void BiasLearner::processTranscript(const std::vector<double>& startHist, const
 		_endPosExp(currEndBin, lenClass) += !(_frag_len_dist->too_short(i+1));
 
 		
-		bool start_in_bounds = i-CENTER >= 0 && i+(_M-1)-CENTER < seqLen;
-		bool end_in_bounds = i+CENTER-(_M-1) >= 0 && i+CENTER < seqLen;
+		bool start_in_bounds = i-CENTER >= 0 && i+(_m-1)-CENTER < seqLen;
+		bool end_in_bounds = i+CENTER-(_m-1) >= 0 && i+CENTER < seqLen;
 		
 		if (!start_in_bounds && !end_in_bounds) // Make sure we are in bounds of the sequence
 			continue;
 		
 		//Sequence Bias
-		for(int j=0; j < _M; j++)
+		for(int j=0; j < _m; j++)
 		{
 			// Start Bias
 			if (start_in_bounds) // Make sure we are in bounds of the sequence
@@ -420,7 +354,7 @@ void BiasLearner::getBias(const Scaffold& transcript, vector<double>& startBiase
 	for (int i=0; i < seqLen; i++)
 	{
 		//Position Bias
-		if (i > startBinCutoff && currStartBin < _startPosParams.size1()-1)
+		if (i > startBinCutoff && currStartBin < (int)_startPosParams.size1()-1)
 			startBinCutoff=positionBins[++currStartBin]*(seqLen - min_frag_len);
 		if (i - min_frag_len > endBinCutoff)
 			endBinCutoff = positionBins[++currEndBin]*(seqLen - min_frag_len);
@@ -430,12 +364,12 @@ void BiasLearner::getBias(const Scaffold& transcript, vector<double>& startBiase
 		
 		//Sequence Bias
 		
-		bool start_in_bounds = i-CENTER >= 0 && i+(_M-1)-CENTER < seqLen;
-		bool end_in_bounds = i+CENTER-(_M-1) >= 0 && i+CENTER < seqLen - _frag_len_dist->mean(); // don't count bias near end since we're over-counting these fragments
+		bool start_in_bounds = i-CENTER >= 0 && i+(_m-1)-CENTER < seqLen;
+		bool end_in_bounds = i+CENTER-(_m-1) >= 0 && i+CENTER < seqLen - _frag_len_dist->mean(); // don't count bias near end since we're over-counting these fragments
 		
 		if (start_in_bounds || end_in_bounds) // Make sure we are in bounds of the sequence
 		{
-			for(int j=0; j < _M; j++)
+			for(int j=0; j < _m; j++)
 			{
 				// Start Bias
 				if (start_in_bounds) // Make sure we are in bounds of the sequence
@@ -572,7 +506,7 @@ void BiasLearner::normalizeParameters()
 	fourSums(_endSeqExp, endSeqExp_sums); 
 	
 	//Normalize sequence parameters
-	for(int i=0; i < _M; i++)
+	for(int i=0; i < _m; i++)
 	{
 		for(int j=0; j < pow4[paramTypes[i]]; j++)
 		{
@@ -605,12 +539,12 @@ void BiasLearner::normalizeParameters()
 	if (end_tot==0.0)
 		ones(_endSeqParams);
 	
-	if (bias_mode=="vlmm" || bias_mode=="site")
+	if (bias_mode==VLMM || bias_mode==SITE)
 	{
 		ones(_startPosParams);
 		ones(_endPosParams);
 	}
-	else if (bias_mode =="pos")	
+	else if (bias_mode == POS)	
 	{
 		ones(_startSeqParams);
 		ones(_endSeqParams);
@@ -624,18 +558,18 @@ void BiasLearner::output()
 	myfile1.open (filename.c_str());
 	
 	// StartSeq
-	for (int i = 0; i < _N; ++i)
+	for (int i = 0; i < _n; ++i)
 	{
-		for(int j = 0; j < _M; ++j)
+		for(int j = 0; j < _m; ++j)
 			myfile1 << _startSeqParams(j,i) <<",";
 		myfile1 << endl;
 	}
 	myfile1 << endl;
 	
 	// EndSeq
-	for (int i = 0; i < _N; ++i)
+	for (int i = 0; i < _n; ++i)
 	{
-		for(int j = 0; j < _M; ++j)
+		for(int j = 0; j < _m; ++j)
 			myfile1 << _endSeqParams(j,i) <<",";
 		myfile1 << endl;
 	}
@@ -673,12 +607,7 @@ int BiasCorrectionHelper::add_read_group(shared_ptr<ReadGroupProperties const> r
 	vector<double> end_bias(trans_len+1, 1.0);
 	double eff_len = 0.0;
 	
-	if (rgp->bias_learner()!=NULL && _transcript->strand()!=CUFF_STRAND_UNKNOWN)
-	{
-		rgp->bias_learner()->getBias(*_transcript, start_bias, end_bias);
-	}
-	
-	shared_ptr<EmpDist const> frag_len_dist = rgp->frag_len_dist();
+	shared_ptr<EmpDist const> fld = rgp->frag_len_dist();
 	
 	vector<double> tot_bias_for_len(trans_len+1, 0);
 	vector<double> start_bias_for_len(trans_len+1, 0);
@@ -688,27 +617,35 @@ int BiasCorrectionHelper::add_read_group(shared_ptr<ReadGroupProperties const> r
 	start_bias_for_len[trans_len] = trans_len;
 	end_bias_for_len[trans_len] = trans_len;
 
-	
-	for(int l = rgp->frag_len_dist()->min(); l <= trans_len; l++)
+	if (final_est_run && corr_bias && _transcript->strand()!=CUFF_STRAND_UNKNOWN)
+	{
+		rgp->bias_learner()->getBias(*_transcript, start_bias, end_bias);
+
+		for(int l = fld->min(); l <= trans_len; l++)
+		{
+			for(int i = 0; i <= trans_len - l; i++)
+			{
+				double tot_bias = start_bias[i]*end_bias[i+l-1];
+				tot_bias_for_len[l] += tot_bias;
+				start_bias_for_len[l] += start_bias[i];
+				end_bias_for_len[l] += end_bias[i+l-1];
+				
+				double frag_prob = (bias_mode == POS || bias_mode == POS_VLMM || bias_mode == POS_SITE) ? fld->npdf(l, trans_len-i) : fld->pdf(l);
+				eff_len += tot_bias * frag_prob;
+			}
+		}
+	}
+	else
 	{
-		//double tot = 0;
-		//double start = 0;
-		//double end = 0;
-		for(int i = 0; i <= trans_len - l; i++)
+		for(int l = fld->min(); l <= trans_len; l++)
 		{
-			double tot_bias = start_bias[i]*end_bias[i+l-1];
-			tot_bias_for_len[l] += tot_bias;
-			start_bias_for_len[l] += start_bias[i];
-			end_bias_for_len[l] += end_bias[i+l-1];
-			eff_len += tot_bias * frag_len_dist->npdf(l, trans_len - i);
+			tot_bias_for_len[l] = trans_len - l + 1;
+			start_bias_for_len[l] = trans_len - l + 1;
+			end_bias_for_len[l] = trans_len - l + 1;
+			eff_len += fld->pdf(l) * (trans_len - l + 1);
 		}
-        //assert(tot != 0);
-		//tot_bias_for_len[l] = tot;
-		//eff_len += tot * p_len;
-		//mean_start_bias += start * p_len;
-		//mean_end_bias += end * p_len; 
 	}
-	
+		
 	_start_biases.push_back(start_bias);
 	_end_biases.push_back(end_bias);
 	_tot_biases_for_len.push_back(tot_bias_for_len);
@@ -735,7 +672,7 @@ int BiasCorrectionHelper::get_index(shared_ptr<ReadGroupProperties const> rgp)
 	return iter->second;
 }
 
-// Hit needs to be collapsed
+// Hit needs to be from the collapsed (non_redundant) list to match indexing 
 double BiasCorrectionHelper::get_cond_prob(const MateHit& hit)
 {
 	shared_ptr<ReadGroupProperties const> rgp = hit.read_group_props();
@@ -754,28 +691,84 @@ double BiasCorrectionHelper::get_cond_prob(const MateHit& hit)
 	double cond_prob = 1.0;
 	cond_prob *= _start_biases[i][start];
 	cond_prob *= _end_biases[i][end];
-	cond_prob *= fld->npdf(frag_len, trans_len-start); //defaults to pdf if trans_len==start
+	double frag_prob = (bias_mode == POS || bias_mode == POS_VLMM || bias_mode == POS_SITE) ? fld->npdf(frag_len, trans_len-start) : fld->pdf(frag_len);
+	cond_prob *= frag_prob; 
 	
 	if (cond_prob==0.0)
 		return 0.0;
 	
-	if (hit.is_pair())
-		cond_prob /= _tot_biases_for_len[i][frag_len];
+    if (hit.is_pair())
+    {
+        if (frag_len >= (int)_tot_biases_for_len[i].size())
+            cond_prob = 0.0;
+        else
+            cond_prob /= _tot_biases_for_len[i][frag_len];
+    }
 	else if (start!=trans_len && end==trans_len) // The hit is a singleton at the start of a fragment
 		cond_prob /= _start_biases_for_len[i][frag_len];
 	else if (start==trans_len && end!=trans_len) // The hit is a singleton at the end of a fragment
 		cond_prob /= _end_biases_for_len[i][frag_len];
 	else if (frag_len==trans_len)  // We don't actually know where we start or end and can't subtract off the frag_len or we'll get inf
 		cond_prob /= trans_len;
-	else // Single-end read w/ library type FF or RR
-		cond_prob /= trans_len-frag_len;
+	else
+    {
+        if (trans_len < frag_len)
+        {
+            cond_prob = 0;
+        }
+        else 
+        {
+            // Single-end read w/ library type FF or RR
+            cond_prob /= trans_len-frag_len;
+        }
+    }
 
 	if (cond_prob > 0 && hit.collapse_mass() > 0)
 	{
-		_rg_masses[i] += hit.collapse_mass();
+		_rg_masses[i] +=  hit.collapse_mass();
 		_mapped = true;
 	}
 	
+#if DEBUG
+    if (isinf(cond_prob))
+    {
+        double cond_prob = 1.0;
+        cond_prob *= _start_biases[i][start];
+        cond_prob *= _end_biases[i][end];
+        double frag_prob = (bias_mode == POS || bias_mode == POS_VLMM || bias_mode == POS_SITE) ? fld->npdf(frag_len, trans_len-start) : fld->pdf(frag_len);
+        cond_prob *= frag_prob; 
+        
+        if (cond_prob==0.0)
+            return 0.0;
+        
+        if (hit.is_pair())
+        {
+            if (frag_len >= _tot_biases_for_len[i].size())
+                cond_prob = 0.0;
+            else
+                cond_prob /= _tot_biases_for_len[i][frag_len];
+        }
+        else if (start!=trans_len && end==trans_len) // The hit is a singleton at the start of a fragment
+            cond_prob /= _start_biases_for_len[i][frag_len];
+        else if (start==trans_len && end!=trans_len) // The hit is a singleton at the end of a fragment
+            cond_prob /= _end_biases_for_len[i][frag_len];
+        else if (frag_len==trans_len)  // We don't actually know where we start or end and can't subtract off the frag_len or we'll get inf
+            cond_prob /= trans_len;
+        else
+        {
+            if (trans_len < frag_len)
+            {
+                cond_prob = 0;
+            }
+            else 
+            {
+                // Single-end read w/ library type FF or RR
+                cond_prob /= trans_len-frag_len;
+            }
+        }
+    }
+#endif
+    
 	assert(!isinf(cond_prob));
 	assert(!isnan(cond_prob));
 	return cond_prob;
@@ -804,6 +797,7 @@ double BiasCorrectionHelper::get_effective_length()
 	}
 	
 	assert(eff_len>0);
+    //assert(eff_len>1);
 	assert(!isnan(eff_len));
 	return eff_len;
 }
diff --git a/src/biascorrection.h b/src/biascorrection.h
index eafe48f..a66d643 100644
--- a/src/biascorrection.h
+++ b/src/biascorrection.h
@@ -11,27 +11,31 @@
  */
 
 
-#include "bundles.h"
 #include <boost/numeric/ublas/matrix.hpp>
 #include <vector>
 #include <list>
 #include <string>
 #include <boost/tr1/unordered_map.hpp>
 #include <boost/thread.hpp>
+#include "common.h"
+
+class MateHit;
+class Scaffold;
+class BundleFactory;
+class HitBundle;
 
 namespace ublas = boost::numeric::ublas;
-using namespace std;
 
-void get_compatibility_list(const vector<Scaffold>& transcripts,
-							const vector<MateHit>& alignments,
-							vector<list<int> >& compatibilities);
+void get_compatibility_list(const std::vector<Scaffold>& transcripts,
+							const std::vector<MateHit>& alignments,
+							std::vector<std::list<int> >& compatibilities);
 
 class BiasLearner{
 	static const int pow4[];
 	static const int MAX_SLICE;
 	static const int CENTER;
-	static const int _M;
-	static const int _N;
+	static const int _m;
+	static const int _n;
 	
 	static const int lengthBins[];
 	static const double positionBins[];
@@ -39,7 +43,7 @@ class BiasLearner{
 	static const int vlmmSpec[];
 	
 	const int* paramTypes;
-	shared_ptr<EmpDist const> _frag_len_dist;
+	boost::shared_ptr<EmpDist const> _frag_len_dist;
 	ublas::matrix<long double> _startSeqParams;
 	ublas::matrix<long double> _startSeqExp;
 	ublas::matrix<long double> _endSeqParams;
@@ -51,7 +55,7 @@ class BiasLearner{
 
 	int seqToInt(const char* seqSlice, int n) const;
 	void getSlice(const char* seq, char* slice, int start, int end) const;
-	void genNList(const char* seqSlice, int start, int n, list<int>& nList) const;
+	void genNList(const char* seqSlice, int start, int n, std::list<int>& nList) const;
 
 #if ENABLE_THREADS	
 	boost::mutex _bl_lock;
@@ -59,14 +63,14 @@ class BiasLearner{
 	
 public:
 	
-	BiasLearner(shared_ptr<EmpDist const> frag_len_dist);
+	BiasLearner(boost::shared_ptr<EmpDist const> frag_len_dist);
 	void preProcessTranscript(const Scaffold& transcript);
 	
-	void processTranscript(const vector<double>& startHist, const vector<double>& endHist, const Scaffold& transcript);
+	void processTranscript(const std::vector<double>& startHist, const std::vector<double>& endHist, const Scaffold& transcript);
 	void normalizeParameters();
 	void output();
 	
-	void getBias(const Scaffold& transcript, vector<double>& startBiases, vector<double>& endBiases) const;
+	void getBias(const Scaffold& transcript, std::vector<double>& startBiases, std::vector<double>& endBiases) const;
 
 };
 
@@ -76,27 +80,27 @@ void process_bundle(HitBundle& bundle, BiasLearner& bl);
 // Helps with the complexities of bias correction with replicates in cond_probs and eff_lens
 class BiasCorrectionHelper{
 	
-	shared_ptr<Scaffold> _transcript;
-    boost::unordered_map<shared_ptr<ReadGroupProperties const>, int> _rg_index;
+	boost::shared_ptr<Scaffold> _transcript;
+    boost::unordered_map<boost::shared_ptr<ReadGroupProperties const>, int> _rg_index;
 	int _size;
 	bool _mapped;
 	
-	vector<vector<double> > _start_biases;
-	vector<vector<double> > _end_biases;
-	vector<vector<double> > _pos_biases;
-	vector<vector<double> > _tot_biases_for_len;
-	vector<vector<double> > _start_biases_for_len;
-	vector<vector<double> > _end_biases_for_len;
+	std::vector<std::vector<double> > _start_biases;
+	std::vector<std::vector<double> > _end_biases;
+	std::vector<std::vector<double> > _pos_biases;
+	std::vector<std::vector<double> > _tot_biases_for_len;
+	std::vector<std::vector<double> > _start_biases_for_len;
+	std::vector<std::vector<double> > _end_biases_for_len;
 	
-	vector<double> _eff_lens;
-	vector<double> _rg_masses;
+	std::vector<double> _eff_lens;
+	std::vector<double> _rg_masses;
 	
-	int add_read_group(shared_ptr<ReadGroupProperties const> rgp);	
-	int get_index(shared_ptr<ReadGroupProperties const> rgp);
+	int add_read_group(boost::shared_ptr<ReadGroupProperties const> rgp);	
+	int get_index(boost::shared_ptr<ReadGroupProperties const> rgp);
 	
 public:
 	
-	BiasCorrectionHelper(shared_ptr<Scaffold> transcript) 
+	BiasCorrectionHelper(boost::shared_ptr<Scaffold> transcript) 
 	{ 
 		_transcript = transcript;
 		_mapped = false;
diff --git a/src/bundles.cpp b/src/bundles.cpp
index 14702ce..fca869a 100644
--- a/src/bundles.cpp
+++ b/src/bundles.cpp
@@ -79,23 +79,25 @@ void load_ref_rnas(FILE* ref_mRNA_file,
 				   bool loadFPKM) 
 {
 	if (loadSeqs)
-		ProgressBar p_bar("Loading reference and sequence.",0);
-    
+		ProgressBar p_bar("Loading reference annotation and sequence.",0);
+    else
+        ProgressBar p_bar("Loading reference annotation.",0);
+
 	GList<GSeqData> ref_rnas;
 	
-    // If the RefSequenceTable already has entries, we will sort the GTF records
-    // according to their observation order.  Otherwise, we will sort the 
-    // RefSequenceTable's records lexicographically.
-    bool reorder_GTF_recs_lexicographically = false;
-    if (rt.size() == 0)
-    {
-        reorder_GTF_recs_lexicographically = true;
-    }
-    
+	// If the RefSequenceTable already has entries, we will sort the GTF records
+	// according to their observation order.  Otherwise, we will sort the 
+	// RefSequenceTable's records lexicographically.
+	bool reorder_GTF_recs_lexicographically = false;
+	if (rt.size() == 0)
+	{
+	  reorder_GTF_recs_lexicographically = true;
+	}
+
 	if (ref_mRNA_file)
 	{
-		read_transcripts(ref_mRNA_file, ref_rnas);
-        //read_mRNAs(ref_mRNA_file, ref_rnas, &ref_rnas, true, -1, "", false);
+		gtf_tracking_verbose=cuff_verbose;
+		read_transcripts(ref_mRNA_file, ref_rnas, true);
 	}
 	
 	int last_gseq_id = -1;
@@ -117,16 +119,15 @@ void load_ref_rnas(FILE* ref_mRNA_file,
 			int r_count = ref_rnas[j]->mrnas_r.Count();
 			int u_count = ref_rnas[j]->umrnas.Count();
 			
-			
 			while(!(f==f_count && r==r_count && u==u_count))
 			{	
 				CuffStrand strand;
 				
 				if (f < f_count)
-                {
+				{
 					rna_p = ref_rnas[j]->mrnas_f[f++];
 					strand = CUFF_FWD;
-                }
+				}
 				else if (r < r_count) 
 				{
 					rna_p = ref_rnas[j]->mrnas_r[r++];
@@ -137,11 +138,9 @@ void load_ref_rnas(FILE* ref_mRNA_file,
 					rna_p = ref_rnas[j]->umrnas[u++];
 					strand = CUFF_STRAND_UNKNOWN;
 				}
-				
-				
+
 				GffObj& rna = *rna_p;
-				
-                
+
 				if (loadSeqs && rna.gseq_id != last_gseq_id) //next chromosome
 				{
 					delete faseq;
@@ -167,24 +166,23 @@ void load_ref_rnas(FILE* ref_mRNA_file,
 					}
 				}
 				
-				Scaffold ref_scaff(ref_id, strand, ops);
+				Scaffold ref_scaff(ref_id, strand, ops, true);
 				
 				char* rna_seq;
 				int seqlen=0;
 				if (loadSeqs && faseq){ 
 					rna_seq = rna.getSpliced(faseq, false, &seqlen);
 				}
-                
+
 				if (rna.getID())
 					ref_scaff.annotated_trans_id(rna.getID());
 				
 				
-				if (rna.getGene())
-					ref_scaff.annotated_gene_id(rna.getGene());
+				if (rna.getGeneID())
+					ref_scaff.annotated_gene_id(rna.getGeneID());
 				
-				char* short_name = rna.getAttr("gene_name");
-				if (short_name)
-					ref_scaff.annotated_gene_name(short_name);
+				if (rna.getGeneName())
+					ref_scaff.annotated_gene_name(rna.getGeneName());
 				
 				
 				char* nearest_ref_match = rna.getAttr("nearest_ref");
@@ -263,6 +261,7 @@ bool HitBundle::add_hit(const MateHit& hit)
 	if (hit.right() > _rightmost)
 		_rightmost = hit.right();
 	
+	
 	_hits.push_back(hit);
 	return true;
 }
@@ -281,20 +280,33 @@ bool unmapped_hit(const MateHit& x)
 }
 
 
-void HitBundle::add_open_hit(shared_ptr<ReadGroupProperties const> rg_props,
-                             const ReadHit* bh)
-{
-	if (bh->partner_ref_id() == 0 
-		|| bh->partner_ref_id() != bh->ref_id() ||
-		abs((int)bh->partner_pos() - (int)bh->left()) > max_partner_dist)
+bool HitBundle::add_open_hit(shared_ptr<ReadGroupProperties const> rg_props,
+                             const ReadHit* bh,
+							 bool expand_by_partner)
+{	
+	_leftmost = min(_leftmost, bh->left());
+	_ref_id = bh->ref_id();
+    
+	if (bh->is_singleton())
 	{
-		// This is a singleton, so just make a closed MateHit and
-		// continue
+		_rightmost = max(_rightmost, bh->right());
 		MateHit m(rg_props, bh->ref_id(), bh, NULL);
+        if (m.right() - m.left() > max_gene_length)
+        {
+            fprintf(stderr, "Warning: hit is longer than max_gene_length, skipping\n");
+            return false;
+        }
 		add_hit(m);
 	}
 	else
 	{
+        if (abs(bh->right() - bh->partner_pos()+1) > max_gene_length)
+        {
+            fprintf(stderr, "Warning: hit is longer than max_gene_length, skipping\n");
+            return false;
+        }
+		if (expand_by_partner)
+			_rightmost = max(max(_rightmost, bh->right()), bh->partner_pos()+1);
 		OpenMates::iterator mi = _open_mates.find(bh->left());
 		
 		// Does this read hit close an open mate?
@@ -302,7 +314,7 @@ void HitBundle::add_open_hit(shared_ptr<ReadGroupProperties const> rg_props,
 		{
 			// No, so add it to the list of open mates, unless we would
 			// already have seen it's partner
-			if(bh->left() < bh->partner_pos())
+			if(bh->left() <= bh->partner_pos())
 			{
 				MateHit open_hit(rg_props,
                                  bh->ref_id(), 
@@ -317,7 +329,11 @@ void HitBundle::add_open_hit(shared_ptr<ReadGroupProperties const> rg_props,
 			}
 			else
 			{
-				add_hit(MateHit(rg_props,bh->ref_id(), bh, NULL));
+                // This should never happen during hit_driven or ref_guided bundling, and in the case of
+                // ref_driven, this read clearly shouldn't map to any of the transcripts anyways.
+                // Adding this hit would cause problems with multi-reads that straddle boundaries after assembly.
+				// add_hit(MateHit(rg_props,bh->ref_id(), bh, NULL));
+                return false;
 			}
 		}
 		else
@@ -366,7 +382,7 @@ void HitBundle::add_open_hit(shared_ptr<ReadGroupProperties const> rg_props,
 				// If we got here, couldn't actually close any mates with
 				// this read hit, so open a new one, unless we can never
 				// close this one
-				if(bh->left() < bh->partner_pos())
+				if(bh->left() <= bh->partner_pos())
 				{
 					MateHit open_hit(rg_props, bh->ref_id(), bh, NULL);
 					
@@ -378,57 +394,36 @@ void HitBundle::add_open_hit(shared_ptr<ReadGroupProperties const> rg_props,
 				}
 				else
 				{
-					add_hit(MateHit(rg_props, bh->ref_id(), bh, NULL));
+                    // This should never happen during hit_driven or ref_guided bundling, and in the case of
+                    // ref_driven, this read clearly shouldn't map to any of the transcripts anyways.
+                    // Adding this hit would cause problems with multi-reads that straddle boundaries after assembly.
+					// add_hit(MateHit(rg_props, bh->ref_id(), bh, NULL));
+                    return false;
 				}
 			}
 		}
 	}
+    return true;
 }
 
-
 void HitBundle::collapse_hits()
 {
-	copy(_hits.begin(), _hits.end(), back_inserter(_non_redundant));
-	vector<MateHit>::iterator new_end = unique(_non_redundant.begin(), 
-											   _non_redundant.end(), 
-											   hits_equals);
-	_non_redundant.erase(new_end, _non_redundant.end());
-    _non_redundant.resize(_non_redundant.size());
-	
-	foreach(MateHit& hit, _non_redundant)
-		hit.collapse_mass(0);
-	
-	size_t curr_aln = 0;
-	size_t curr_unique_aln = 0;
-	while (curr_aln < _hits.size())
-	{
-		if (hits_equals(_non_redundant[curr_unique_aln], _hits[curr_aln]) || hits_equals(_non_redundant[++curr_unique_aln], _hits[curr_aln]))
-		{
-			MateHit& unique_aln = _non_redundant[curr_unique_aln];
-			unique_aln.incr_collapse_mass(_hits[curr_aln].mass());
-			_hits[curr_aln].collapsed_to(&unique_aln);
-		}
-		else
-			assert(false);
-		
-		++curr_aln;
-	}
-	
-	//_non_redundant.erase(remove_if(_non_redundant.begin(),_non_redundant.end(),has_no_collapse_mass), _non_redundant.end()); 
+	::collapse_hits(_hits, _non_redundant);
 }
 
 void HitBundle::finalize_open_mates()
 {
-	for (OpenMates::iterator itr = _open_mates.begin(); 
-		 itr != _open_mates.end(); 
-		 ++itr)
-	{
-		for (list<MateHit>::iterator mi = itr->second.begin(); mi != itr->second.end(); ++mi)
-		{
-			add_hit(*mi);
-		}
-        itr->second.clear();
-	}
+    // We don't want to split reads accross boundaries since this would only occur
+    // in ref_driven mode and the read shouldn't map to any of the references in this case.
+
+    for(OpenMates::iterator itr = _open_mates.begin(); itr != _open_mates.end(); ++itr)
+    {
+        foreach (MateHit& hit,  itr->second)
+        {
+            delete hit.left_alignment();
+            delete hit.right_alignment();
+        }
+    }
     _open_mates.clear();
 }
 
@@ -478,12 +473,12 @@ void HitBundle::combine(const vector<HitBundle*>& in_bundles,
     }
     
     // Merge  hits
-    vector<int> indices(in_bundles.size(),0);
+    vector<size_t> indices(in_bundles.size(),0);
     while(true)
     {
         int next_bundle = -1;
         const MateHit* next_hit=NULL; 
-        for(int i = 0; i < in_bundles.size(); ++i)
+        for(size_t i = 0; i < in_bundles.size(); ++i)
         {
             const vector<MateHit>& curr_hits = in_bundles[i]->hits();
             
@@ -507,12 +502,12 @@ void HitBundle::combine(const vector<HitBundle*>& in_bundles,
     }
     
     // Merge collapsed hits
-    indices = vector<int>(in_bundles.size(), 0);
+    indices = vector<size_t>(in_bundles.size(), 0);
     while(true)
     {
         int next_bundle = -1;
         const MateHit* next_hit = NULL; 
-        for(int i = 0; i < in_bundles.size(); ++i)
+        for(size_t i = 0; i < in_bundles.size(); ++i)
         {
             const vector<MateHit>& curr_non_redundant_hits = in_bundles[i]->non_redundant_hits();
             
@@ -535,7 +530,7 @@ void HitBundle::combine(const vector<HitBundle*>& in_bundles,
         indices[next_bundle]++;
     }
     
-    for(int i = 0; i < in_bundles.size(); ++i)
+    for(size_t i = 0; i < in_bundles.size(); ++i)
     {
         for (size_t j = 0; j < in_bundles[i]->_ref_scaffs.size(); ++j)
         {
@@ -544,12 +539,12 @@ void HitBundle::combine(const vector<HitBundle*>& in_bundles,
     }
     
     // Merge ref scaffolds
-    indices = vector<int>(in_bundles.size(), 0);
+    indices = vector<size_t>(in_bundles.size(), 0);
     while(true)
     {
         int next_bundle = -1;
         shared_ptr<Scaffold> next_scaff; 
-        for(int i = 0; i < in_bundles.size(); ++i)
+        for(size_t i = 0; i < in_bundles.size(); ++i)
         {
             const vector<shared_ptr<Scaffold> >& curr_scaffs = in_bundles[i]->_ref_scaffs;
             
@@ -569,10 +564,10 @@ void HitBundle::combine(const vector<HitBundle*>& in_bundles,
             break;
         
         if (out_bundle._ref_scaffs.size()==0 || out_bundle._ref_scaffs.back()->annotated_trans_id() != next_scaff->annotated_trans_id()) 
-            out_bundle._ref_scaffs.push_back(next_scaff);
+            out_bundle.add_ref_scaffold(next_scaff);
         indices[next_bundle]++;
     }
-    
+	
     out_bundle.finalize(true); // true means everything is already sorted, etc.
     out_bundle._num_replicates = (int)in_bundles.size();
 }
@@ -599,6 +594,8 @@ void HitBundle::finalize(bool is_combined)
 		_ref_scaffs[j]->clear_hits();
 	}
     
+    _compatible_mass = 0.0;
+    
 	for (size_t i = 0; i < _hits.size(); ++i)
 	{
 		MateHit& hit = _hits[i];
@@ -620,21 +617,12 @@ void HitBundle::finalize(bool is_combined)
                     hit.is_mapped(true);
 			}
 		}
+        if (hit.is_mapped())
+        {
+            _compatible_mass += hit.mass();
+        }
 	}
     
-	if (_ref_scaffs.size() > 0)
-    {
-        _leftmost = INT_MAX;
-        _rightmost = -1;
-    }
-    
-	for (size_t i = 0; i < _ref_scaffs.size(); ++i)
-	{
-		if (_ref_scaffs[i]->left() < _leftmost)
-			_leftmost = _ref_scaffs[i]->left();
-		if (_ref_scaffs[i]->right() > _rightmost)
-			_rightmost = _ref_scaffs[i]->right();
-	}
 }
 
 void print_sort_error(const char* last_chr_name, 
@@ -651,12 +639,16 @@ void print_sort_error(const char* last_chr_name,
     fprintf(stderr, "Cufflinks requires that if your file has SQ records in\nthe SAM header that they appear in the same order as the chromosomes names \nin the alignments.\nIf there are no SQ records in the header, or if the header is missing,\nthe alignments must be sorted lexicographically by chromsome\nname and by position.\n \n");
 }
 
-const ReadHit* BundleFactory::next_valid_alignment()
+
+double BundleFactory::next_valid_alignment(const ReadHit*& bh)
 {
     const char* hit_buf;
 	size_t hit_buf_size = 0;
-    const ReadHit* bh = NULL;
+    bh = NULL;
     
+	// Keep track of mass of hits we skip
+	double raw_mass = 0; 
+	
     while (true)
     {
     
@@ -667,12 +659,11 @@ const ReadHit* BundleFactory::next_valid_alignment()
         if (!_hit_fac->get_hit_from_buf(hit_buf, tmp, false))
             continue;
         
-        if (tmp.error_prob() > max_phred_err_prob)
-            continue;
-        
-        if (tmp.ref_id() == 84696373) // corresponds to SAM "*" under FNV hash. unaligned read record  
+		if (tmp.ref_id() == 12638153115695167477)  // corresponds to SAM "*" under FNV hash. unaligned read record 
             continue;
-
+	
+		raw_mass += tmp.mass();
+		
         if (_hit_fac->ref_table().get_name(tmp.ref_id())==NULL) // unaligned read record (!?)
             continue;
             
@@ -688,9 +679,7 @@ const ReadHit* BundleFactory::next_valid_alignment()
             {
                 const char* bh_chr_name = _hit_fac->ref_table().get_name(tmp.ref_id());
                 const char* last_bh_chr_name = _hit_fac->ref_table().get_name(_prev_ref_id);
-                
-                _hit_fac->ref_table().observation_order(_prev_ref_id);
-                
+                                
                 print_sort_error(last_bh_chr_name, 
                                  _prev_pos, 
                                  bh_chr_name, 
@@ -752,365 +741,277 @@ const ReadHit* BundleFactory::next_valid_alignment()
         break;
     }
     
-    return bh;
+    return raw_mass;
 }
 
-// This is my least favorite function in Cufflinks.  It should be banished to
-// Hell and re-written.  It is utterly loathesome.
-bool BundleFactory::next_bundle(HitBundle& bundle)
+double BundleFactory::rewind_hit(const ReadHit* rh)
 {
-	//char bwt_buf[2048];
-	
-    // The most recent RefID and position we've seen in the hit stream
-	RefID last_hit_ref_id_seen = 0;
-    int last_hit_pos_seen = 0;
-    
-    // The most recent RefID and position we've seen in the reference RNA stream
-	RefID last_ref_rna_ref_id_seen = 0;
-    int last_ref_rna_pos_seen = 0;
-    
-    // The first RefID we saw in either stream
-	RefID first_ref_id_seen = 0;
+	double mass = rh->mass();
+	delete rh;
+	_hit_fac->undo_hit();
+	return mass;
+}
 
-	
-	int right_bundle_boundary = 0;
-	int left_bundle_boundary = -1;
-	
-    
-	while(true)
+bool BundleFactory::next_bundle_hit_driven(HitBundle& bundle)
+{
+	const ReadHit* bh = NULL;
+	while(bh == NULL)
 	{
-        const ReadHit* bh = next_valid_alignment();
-        
-
-		// Initialize the bundle boundaries using the hit or the next 
-        // reference transcript, whichever is first in the stream
-		if (left_bundle_boundary == -1)
-        {
-            if (_ref_driven && next_ref_scaff != ref_mRNAs.end() && bh != NULL) // We're not done
-            {
-                if (bh->ref_id() != (*next_ref_scaff)->ref_id())
-                {
-                    const char* bh_chr_name = _hit_fac->ref_table().get_name(bh->ref_id());
-                    const char* ref_rna_chr_name = _hit_fac->ref_table().get_name((*next_ref_scaff)->ref_id());
-                    const char* last_bh_chr_name = _hit_fac->ref_table().get_name(last_hit_ref_id_seen);
-                    
-                    int bh_chr_order = _hit_fac->ref_table().observation_order(bh->ref_id());
-                    int ref_rna_chr_order = _hit_fac->ref_table().observation_order((*next_ref_scaff)->ref_id());
-                    int last_bh_chr_order = _hit_fac->ref_table().observation_order(last_hit_ref_id_seen);
-                    
-                    // if the BAM/SAM file isn't lexicographically sorted by chromosome, print an error
-                    // and exit.
-                    if (last_bh_chr_name && last_bh_chr_order > bh_chr_order)
-                    { 
-                        print_sort_error(last_bh_chr_name, 
-                                         last_hit_pos_seen, 
-                                         bh_chr_name, 
-                                         bh->left());
-                        exit(1);
-                    }
-                    
-                    if (bh_chr_name && ref_rna_chr_name)
-                    {
-                        if (bh_chr_order < ref_rna_chr_order)
-                        {
-                            // hit is lexicographically less than the reference,
-                            // so skip the hit.
-                            delete bh;
-                            continue;
-                        }
-                        else 
-                        {
-                            // reference is lexicographically less than
-                            // the hit, so wait on the hit and emit this bundle.
-                            left_bundle_boundary = (*next_ref_scaff)->left();
-                            right_bundle_boundary = (*next_ref_scaff)->right();
-                            first_ref_id_seen = (*next_ref_scaff)->ref_id();
-                            
-                        }
-
-                    }
-                    // Then these hits are on a chromosome not in the reference annotation, but which are lexicographically
-                    // less than the next reference record, so just skip them
-                    delete bh;
-                    continue;
-                }
-				
-                if ((bh->ref_id() == (*next_ref_scaff)->ref_id() &&
-                     bh->right() <= (*next_ref_scaff)->left()))
-                {
-                    // if this hit doesn't overlap the next ref transcript
-                    // and falls to its left, we should skip it.  We need
-                    // this to maintain synchronization of multiple
-                    // streams driven by the same set of reference transcripts
-                    
-                    // if the hit stream disagrees with the reference transcript stream
-                    // on which RefID is next, just skip the hit.
-                    delete bh;
-                    continue;
-                }
-                else
-                {
-                    left_bundle_boundary = (*next_ref_scaff)->left();
-                    right_bundle_boundary = (*next_ref_scaff)->right();
-                    first_ref_id_seen = (*next_ref_scaff)->ref_id();
-                }
-
-            }
-            else if (_ref_driven)
-            {
-                if (next_ref_scaff != ref_mRNAs.end())
-                {
-                    left_bundle_boundary = (*next_ref_scaff)->left();
-                    right_bundle_boundary = (*next_ref_scaff)->right();
-                    first_ref_id_seen = (*next_ref_scaff)->ref_id();
-                }
-                else 
-                {
-                    delete bh;
-                    return false;
-                }
-
-            }
-            else if (bh != NULL)
-            {
-                left_bundle_boundary = bh->left();
-                right_bundle_boundary = bh->right();
-                first_ref_id_seen = bh->ref_id();
-            }
-            else
-            {
-                if (!_hit_fac->records_remain() && next_ref_scaff == ref_mRNAs.end())
-                {
-                    delete bh;
-                    return false;
-                }
-                else 
-                {
-                    // if we get here, there are more records, but this one is
-                    // null, and we've reached the end of the reference trancripts
-                    // or there weren't any
-                    delete bh;
-                    continue;
-                }
-            }
-        }
+		if (!_hit_fac->records_remain())
+		{
+			return false;
+		}
+		bundle.add_raw_mass(next_valid_alignment(bh));
+	}
+	
+	if (!bundle.add_open_hit(read_group_properties(), bh))
+    {
+        delete bh;
+        bh = NULL;
+    }
+	_expand_by_hits(bundle);
 
-        assert(left_bundle_boundary != -1);
+    assert(bundle.left() != -1);    
+	bundle.finalize_open_mates();
+	bundle.finalize();
+    assert(bundle.right() != -1);
+    
+    return true;
+}
 
-        // if the hit here overlaps the current bundle interval,
-        // we have to include it, and expand the bundle interval
-        if (bh && !_ref_driven
-            && overlap_in_genome(bh->left(),bh->right(),left_bundle_boundary, right_bundle_boundary + olap_radius))
-        {
-            right_bundle_boundary = max(right_bundle_boundary, bh->right());
-        }
-        
-        // expand the bundle interval as far as needed to include the overlapping
-        // chain of reference transcripts that also overlap the initial bundle
-        // interval, whether the initial bundle interval came from a hit or the
-        // current reference transcript in the GTF record stream.
-		bool hit_within_boundary = false;
-		bool olaps_reference = false;
-		if (_ref_driven)
+bool BundleFactory::next_bundle_ref_driven(HitBundle& bundle)
+{
+	if (next_ref_scaff == ref_mRNAs.end())
+	{
+		const ReadHit* bh = NULL;
+		while(_hit_fac->records_remain())
 		{
-            while(next_ref_scaff < ref_mRNAs.end())
-            {
-                if (first_ref_id_seen == (*next_ref_scaff)->ref_id())
-                {
-                    // if the ref_scaff here overlaps the current bundle interval,
-                    // we have to include it, and expand the bundle interval
-                    if (overlap_in_genome((*next_ref_scaff)->left(),(*next_ref_scaff)->right(),left_bundle_boundary, right_bundle_boundary))
-                    {
-                        right_bundle_boundary = max(right_bundle_boundary, (*next_ref_scaff)->right());
-                        olaps_reference = true;
-                    }
-                    
-                    // If the ref transcript is to the right of the bundle interval, 
-                    // then we can leave this the ref transcript out for now
-                    if ((*next_ref_scaff)->left() >= right_bundle_boundary)
-                    {
-                        // we've gone beyond the bundle and this hit, so we're 
-                        // not going to expand the right boundary any further
-                        // until we see more hits
-                        break;
-                    }
-                }
-                else 
-                {
-                    break;
-                }
-
-                
-                last_ref_rna_ref_id_seen = (*next_ref_scaff)->ref_id();
-                last_ref_rna_pos_seen = (*next_ref_scaff)->left();
-                
-                next_ref_scaff++;
-            }
+			bundle.add_raw_mass(next_valid_alignment(bh));
 		}
-
+        bundle.finalize();
+		return false;
+	}
+	
+	bundle.add_ref_scaffold(*next_ref_scaff);
+	next_ref_scaff++;
+		
+	_expand_by_refs(bundle);
+	
+	// The most recent RefID and position we've seen in the hit stream
+	RefID last_hit_ref_id_seen = 0;
+	int last_hit_pos_seen = 0;
+	
+	// include hits that lay within the bundle interval
+	while(true)
+	{		
+		const ReadHit* bh = NULL;
+		bundle.add_raw_mass(next_valid_alignment(bh));
+		
         if (bh == NULL)
         {
-            break;
-        }
-        
-        bool past_right_end = false;
-        if (last_hit_ref_id_seen == 0 || bh->ref_id() == first_ref_id_seen)
-        {
-            if (bh->left() >= left_bundle_boundary && bh->right() <= right_bundle_boundary)
-            {
-                hit_within_boundary = true;
-            }
-            else if (bh->left() >= right_bundle_boundary)
-            {
-                past_right_end = true;
-            }
-        }
-        else
-        {
-            past_right_end = true;
-            const char* bh_chr_name = _hit_fac->ref_table().get_name(bh->ref_id());
-            const char* last_chr_name = _hit_fac->ref_table().get_name(last_hit_ref_id_seen);
-            
-            int bh_chr_order = _hit_fac->ref_table().observation_order(bh->ref_id());
-            int last_chr_order = _hit_fac->ref_table().observation_order(last_hit_ref_id_seen);
-            
-            if (last_chr_order > bh_chr_order)
-            { 
-                print_sort_error(last_chr_name, 
-                                 last_hit_pos_seen, 
-                                 bh_chr_name, 
-                                 bh->left());
-                exit(1);
-            }
+			if (_hit_fac->records_remain())
+				continue;
+			else
+				break;
         }
-        
-		if (hit_within_boundary)
+				
+		last_hit_ref_id_seen = bh->ref_id();
+		last_hit_pos_seen = bh->left();
+		
+		// test if the hit stream needs to catch up or has gone too far based on ref_id
+		if (bh->ref_id() != bundle.ref_id())
 		{
-			if (bh->left() < last_hit_pos_seen)
+			int bh_chr_order = _hit_fac->ref_table().observation_order(bh->ref_id());
+			int bundle_chr_order = _hit_fac->ref_table().observation_order(bundle.ref_id());
+			
+			if (bh_chr_order < bundle_chr_order) // the hit stream has not caught up, skip
 			{
-                const char* bh_chr_name = _hit_fac->ref_table().get_name(bh->ref_id());
-                const char* last_chr_name = _hit_fac->ref_table().get_name(last_hit_ref_id_seen);
-				print_sort_error(last_chr_name, 
-                                 last_hit_pos_seen,
-                                 bh_chr_name,
-                                 bh->left());
-                delete bh;
-				exit(1);
+				delete bh;
+				continue; 
 			}
-			
-            // We want to drive the bundling entirely by the ref_mRNAs, if they
-            // are there.
-            if (!_ref_driven)
+			else // the hit stream has gone too far, rewind and break
+			{
+				bundle.rem_raw_mass(rewind_hit(bh));
+				break;  
+			}
+		}	
+		
+        if (bh->left() >= bundle.left() && bh->right() <= bundle.right())
+		{
+			if (!bundle.add_open_hit(read_group_properties(), bh, false))
             {
-                if (bh->right() + olap_radius - left_bundle_boundary < (int)max_gene_length)
-                {
-                    right_bundle_boundary = max(right_bundle_boundary, bh->right() + olap_radius);
-                }
-                
-                bool singleton = (bh->partner_ref_id() == 0 
-                                  || bh->partner_ref_id() != bh->ref_id() ||
-                                  abs(bh->partner_pos() - bh->left()) > max_partner_dist);
-                if (!singleton)
-                {
-                    if ((int)bh->partner_pos() + olap_radius - (int)bh->left() < (int)max_partner_dist)
-                    {
-                        right_bundle_boundary = max(right_bundle_boundary, bh->partner_pos() + olap_radius);
-                    }
-                }
+                delete bh;
+                bh = NULL;
             }
-            
-            last_hit_ref_id_seen = bh->ref_id();
-            last_hit_pos_seen = bh->left();
-            
-			bundle.add_open_hit(read_group_properties(), bh);
 		}
-		else if (past_right_end)
+		else if (bh->left() >= bundle.right())
 		{
-            delete bh;
-			_hit_fac->undo_hit();
+			bundle.rem_raw_mass(rewind_hit(bh));
 			break;
 		}
-        else 
+	    else 
         {
             // It's not within the bundle bounds, but it's also not past the 
             // right end, so skip it.
-            last_hit_ref_id_seen = bh->ref_id();
-            last_hit_pos_seen = bh->left();
             delete bh;
         }
-
-		//curr_pos = ftello(hit_file);
 	}
 	
-    assert(left_bundle_boundary != -1);
+    assert(bundle.left() != -1);
+    bundle.finalize_open_mates();
+	bundle.finalize();
+    assert(bundle.right() != -1);
     
-	bundle.finalize_open_mates();
+    return true;
+}
+
+bool BundleFactory::next_bundle_ref_guided(HitBundle& bundle)
+{
 	
-	if (_ref_driven)
+	if (next_ref_scaff == ref_mRNAs.end())
 	{
-		vector<shared_ptr<Scaffold> >::iterator itr = next_ref_scaff;
-		while(itr < ref_mRNAs.end())
+		return next_bundle_hit_driven(bundle);
+	}
+	
+	const ReadHit* bh = NULL;
+	while(bh == NULL)
+	{
+		if (!_hit_fac->records_remain())
 		{
-			if ((*itr)->ref_id() != last_ref_rna_ref_id_seen || (*itr)->left() >= right_bundle_boundary)
-				break;
-			itr++;
+			return next_bundle_ref_driven(bundle);
 		}
+		bundle.add_raw_mass(next_valid_alignment(bh));
+	}
+	
+	if (bh->ref_id() != (*next_ref_scaff)->ref_id())
+	{
+		int bh_chr_order = _hit_fac->ref_table().observation_order(bh->ref_id());
+		int scaff_chr_order = _hit_fac->ref_table().observation_order((*next_ref_scaff)->ref_id());
+		
+		bundle.rem_raw_mass(rewind_hit(bh));
 		
-		bool past_first_id = false;
-		while(itr >= ref_mRNAs.begin())
+		if (bh_chr_order < scaff_chr_order)
 		{
-			if (itr < ref_mRNAs.end())
-			{
-				// if we haven't backed up to the scaffold we need to be on
-				// keep scanning through the reference annotations
-				if ((*itr)->ref_id() == last_ref_rna_ref_id_seen)
-				{
-					// Now we're on the right scaffold, gotta get to the right
-					// coords
+			return next_bundle_hit_driven(bundle);
+		}
+		else
+		{
+			return next_bundle_ref_driven(bundle);
+		}
+	}
+		
+	if (bh->left() < (*next_ref_scaff)->left())
+	{
+		if (!bundle.add_open_hit(read_group_properties(), bh))
+        {
+            delete bh;
+            bh = NULL;
+        }
+	}
+	else 
+	{
+		bundle.rem_raw_mass(rewind_hit(bh));
+		bundle.add_ref_scaffold(*next_ref_scaff);
+		next_ref_scaff++;
+		_expand_by_refs(bundle);
+	}
+	
+	while(_expand_by_hits(bundle) || 
+		  _expand_by_refs(bundle)) {}
+	
+	assert(bundle.left() != -1);    
+	bundle.finalize_open_mates();
+	bundle.finalize();
+	assert(bundle.right() != -1);
+	
+	return true; 
+}
 
-					if (overlap_in_genome((*itr)->left(), 
-                                          (*itr)->right(), 
-                                          left_bundle_boundary, 
-                                          right_bundle_boundary))
+// expand the bundle interval as far as needed to include the overlapping
+// chain of reference transcripts that also overlap the initial bundle
+// interval
+bool BundleFactory::_expand_by_refs(HitBundle& bundle)
+{
+	int initial_right = bundle.right();
+		
+	while(next_ref_scaff < ref_mRNAs.end())
+	{		
+		assert(bundle.ref_id() != (*next_ref_scaff)->ref_id() || (*next_ref_scaff)->left() >= bundle.left());
+		if (bundle.ref_id() == (*next_ref_scaff)->ref_id()
+			&& overlap_in_genome((*next_ref_scaff)->left(),(*next_ref_scaff)->right(),bundle.left(), bundle.right()))
+		{
+			bundle.add_ref_scaffold(*next_ref_scaff++);
+		}
+		else 
+		{
+			break;
+		}		
+	}
 
-					{	
-						bundle.add_ref_scaffold(*itr);
-					}
-					else if ((*itr)->right() < left_bundle_boundary)
-					{	
-						// This reference record is now to the left of 
-						// the bundle of alignments
-						break;
-					}
-				}
-				else if (past_first_id)
-				{
-					break;
-				}
-				
-				// If this reference record is on the same scaffold as we were 
-				// on when we first entered this routine, going back any further
-				// will be unproductive, so it's safe to break out of the loop
-				// as long as the bundle is on a different scaffold.
-				if ((*itr)->ref_id() == first_ref_id_seen)
-				{
-					past_first_id = true;
-				}
+	
+	return (bundle.right() > initial_right);
+}
+
+// expand bundle by chaining overlapping hits
+bool BundleFactory::_expand_by_hits(HitBundle& bundle)
+{
+	int initial_right = bundle.right();
+	while(true)
+	{
+        const ReadHit* bh = NULL;
+		bundle.add_raw_mass(next_valid_alignment(bh));
+		
+		if (bh == NULL)
+		{
+			if (_hit_fac->records_remain())
+			{
+				continue;
 			}
-			--itr;
+			else
+			{
+				break;
+			}	
+		}
+		
+		if (bh->ref_id() == bundle.ref_id() && bh->left() < bundle.right() + olap_radius)
+		{			
+			if (!bundle.add_open_hit(read_group_properties(), bh))
+            {
+                delete bh;
+                bh = NULL;
+            }
+		}
+		else
+		{
+			bundle.rem_raw_mass(rewind_hit(bh));
+			break;
 		}
 	}
 	
-	assert (left_bundle_boundary != -1);
-	bundle.finalize();
-    assert(bundle.right() != -1);
-    
-	//if (_ref_driven) // Must be done after finalize -- only remove unampped if using reference
-	//	bundle.remove_unmapped_hits(); 
-	
-	//bundle_out.remove_hitless_scaffolds();
-    
-    return true;
+	return (bundle.right() > initial_right);
+}
+
+bool BundleFactory::next_bundle(HitBundle& bundle)
+{    
+#if ENABLE_THREADS
+    boost::mutex::scoped_lock lock(_factory_lock);
+#endif
+	switch(_bundle_mode)
+	{
+		case HIT_DRIVEN:
+            _curr_bundle++;
+			return next_bundle_hit_driven(bundle);
+			break;
+		case REF_DRIVEN:
+            _curr_bundle++;
+			return next_bundle_ref_driven(bundle);
+			break;
+		case REF_GUIDED:
+            _curr_bundle++;
+			return next_bundle_ref_guided(bundle);
+			break;
+	}
+	return false;
 }
 
 
@@ -1169,7 +1070,7 @@ void count_introns_in_read(const ReadHit& read,
                 
                 if (read.source_strand() == CUFF_FWD)
                 {
-                    itr->second.fwd_strand_frags;
+                    //itr->second.fwd_strand_frags;
                 }
                 else 
                 {
@@ -1244,7 +1145,7 @@ void minor_introns(int bundle_length,
 			double thresh = itr2_spans.total_reads * fraction;
 			if (doc < thresh)
 			{
-				//#if ASM_VERBOSE
+				//#if verbose_msg
 				//							fprintf(stderr, "\t Filtering intron (due to overlap) %d - %d: %f thresh %f\n", itr->first.first, itr->first.second, doc, bundle_avg_thresh);
 				//#endif	
 				bool exists = binary_search(bad_introns.begin(), 
@@ -1252,7 +1153,7 @@ void minor_introns(int bundle_length,
 											itr->first);
 				if (!exists)
 				{
-					asm_verbose("Filtering intron %d-%d spanned by %lu reads based on overlap with much more abundant intron: %d-%d spanned by %lu reads\n", 
+					verbose_msg("Filtering intron %d-%d spanned by %lu reads based on overlap with much more abundant intron: %d-%d spanned by %lu reads\n", 
 							itr->first.g_left(), 
 							itr->first.g_right(), 
 							itr->second.total_reads,
@@ -1310,7 +1211,7 @@ void multimapping_introns(int bundle_length,
 										itr->first);
 			if (!exists)
 			{
-				asm_verbose("Filtering intron %d-%d spanned by %lu reads because %lg percent are multireads.\n", 
+				verbose_msg("Filtering intron %d-%d spanned by %lu reads because %lg percent are multireads.\n", 
 						itr->first.g_left(), 
 						itr->first.g_right(), 
 						itr->second.total_reads,
@@ -1421,7 +1322,7 @@ void identify_bad_splices(const HitBundle& bundle,
 				double overhang_ratio = counter.little_reads / (double) counter.total_reads;
 				if (counter.total_reads < 100 || overhang_ratio >= 0.50)
 				{
-					asm_verbose("Filtering intron %d-%d spanned by %lu reads (%lu low overhang, %lg expected) left P = %lg, right P = %lg\n", 
+					verbose_msg("Filtering intron %d-%d spanned by %lu reads (%lu low overhang, %lg expected) left P = %lg, right P = %lg\n", 
 							itr->first.g_left(), 
 							itr->first.g_right(), 
 							itr->second.total_reads, 
@@ -1449,7 +1350,7 @@ void identify_bad_splices(const HitBundle& bundle,
 				size_t median = (size_t)floor(hist.size() / 2);
 				if (median <= hist.size() && hist[median] == 0)
 				{
-					asm_verbose("Filtering intron %d-%d spanned by %lu reads (%lu low overhang, %lg expected) left P = %lg, right P = %lg\n", 
+					verbose_msg("Filtering intron %d-%d spanned by %lu reads (%lu low overhang, %lg expected) left P = %lg, right P = %lg\n", 
 							itr->first.g_left(), 
 							itr->first.g_right(), 
 							itr->second.total_reads, 
@@ -1473,7 +1374,7 @@ void identify_bad_splices(const HitBundle& bundle,
 			
 			if (!filtered)
 			{
-				asm_verbose("Accepting intron %d-%d spanned by %lu reads (%lu low overhang, %lg expected) left P = %lg, right P = %lg\n", 
+				verbose_msg("Accepting intron %d-%d spanned by %lu reads (%lu low overhang, %lg expected) left P = %lg, right P = %lg\n", 
 						itr->first.g_left(), 
 						itr->first.g_right(), 
 						itr->second.total_reads, 
diff --git a/src/bundles.h b/src/bundles.h
index 14cc7fc..17eb5fc 100644
--- a/src/bundles.h
+++ b/src/bundles.h
@@ -51,7 +51,7 @@ private:
     HitBundle(const HitBundle& rhs) {} 
 public:
 	HitBundle() 
-    : _leftmost(INT_MAX), _rightmost(-1), _final(false), _id(++_next_id), _num_replicates(1) {}
+    : _leftmost(INT_MAX), _rightmost(-1), _final(false), _id(++_next_id), _ref_id(0), _raw_mass(0.0), _num_replicates(1), _compatible_mass(0.0) {}
 	
     ~HitBundle()
     {
@@ -76,7 +76,16 @@ public:
 		{
 			delete hit.left_alignment();
 			delete hit.right_alignment();
-		} 
+		}
+        
+        for(OpenMates::iterator itr = _open_mates.begin(); itr != _open_mates.end(); ++itr)
+		{
+			foreach (MateHit& hit,  itr->second)
+            {
+                delete hit.left_alignment();
+                delete hit.right_alignment();
+            }
+		}
 		
     }
 	int left()   const { return _leftmost;  }
@@ -86,6 +95,17 @@ public:
 	// Returns true if the hit was added successfully.
 	bool add_hit(const MateHit& hit);
     
+	// This is to keep track of mass of all hits, including
+	// thosethat are not added to any bundle
+	// but are skipped during the creation of this bundle
+	void add_raw_mass(double rm) { _raw_mass += rm; }
+	void rem_raw_mass(double rm) { _raw_mass -= rm; }
+	double raw_mass() { return _raw_mass; }
+    
+    double compatible_mass() const 
+    {
+        return _compatible_mass;
+    }
     
 	void clear_hits() 
     {
@@ -108,30 +128,27 @@ public:
     const std::vector<MateHit>& hits() const { return _hits; } 
 	const std::vector<MateHit>& non_redundant_hits() const { return _non_redundant; } 
 	
-	RefID ref_id()  const
-	{
-		if (!_hits.empty())
-			return _hits.front().ref_id();
-		else if (!_ref_scaffs.empty())
-			return _ref_scaffs.front()->ref_id();
-		else
-			return 0;
-	}
+	RefID ref_id()  const {return _ref_id; }
 	
 	int id() const { return _id; }
 	
 	void add_ref_scaffold(shared_ptr<Scaffold> scaff)
 	{
-        //scaff->clear_hits();
+		if (scaff->left() < _leftmost)
+			_leftmost = scaff->left();
+		if (scaff->right() > _rightmost)
+			_rightmost = scaff->right();
 		_ref_scaffs.push_back(scaff);
+        _ref_id = scaff->ref_id();
 	}
 	
 	vector<shared_ptr<Scaffold> >& ref_scaffolds() { return _ref_scaffs; }
 	
 	// Adds a Bowtie hit to the open hits buffer.  The Bundle will handle turning
 	// the Bowtie hit into a properly mated Cufflinks hit record
-	void add_open_hit(shared_ptr<ReadGroupProperties const> rg_props,
-                      const ReadHit* bh);
+	bool add_open_hit(shared_ptr<ReadGroupProperties const> rg_props,
+                      const ReadHit* bh,
+					  bool expand_by = true);
 	
 	// Commits any mates still open as singleton hits
 	void finalize_open_mates();
@@ -167,12 +184,16 @@ private:
 	std::vector<shared_ptr<Scaffold> > _ref_scaffs; // user-supplied reference annotations overlapping the bundle
 	bool _final;
 	int _id;
+    RefID _ref_id;
+	double _raw_mass;
+
 	
 	static int _next_id;
 	
 	typedef map<int, list<MateHit> > OpenMates;
 	OpenMates _open_mates;
     int _num_replicates;
+    double _compatible_mass;
 };
 
 void load_ref_rnas(FILE* ref_mRNA_file, 
@@ -185,10 +206,25 @@ class BundleFactory
 {
 public:
     
-	BundleFactory(shared_ptr<HitFactory> fac)
-	: _hit_fac(fac), _ref_driven(false), _prev_pos(0), _prev_ref_id(0) {}
+	BundleFactory(shared_ptr<HitFactory> fac, BundleMode bm)
+	: _hit_fac(fac), _bundle_mode(bm), _prev_pos(0), _prev_ref_id(0), _curr_bundle(0)
+	{
+		_rg_props = shared_ptr<ReadGroupProperties>(new ReadGroupProperties(fac->read_group_properties()));
+	}
 
+    bool bundles_remain()  
+    {
+#if ENABLE_THREADS
+        boost::mutex::scoped_lock lock(_factory_lock);
+#endif
+        return _curr_bundle < num_bundles();
+    }
+    
 	bool next_bundle(HitBundle& bundle_out);
+	bool next_bundle_hit_driven(HitBundle& bundle_out);
+	bool next_bundle_ref_driven(HitBundle& bundle_out);
+	bool next_bundle_ref_guided(HitBundle& bundle_out);
+
     
     RefSequenceTable& ref_table() { return _hit_fac->ref_table(); }
     
@@ -198,6 +234,10 @@ public:
     
 	void reset() 
 	{ 
+#if ENABLE_THREADS
+        boost::mutex::scoped_lock lock(_factory_lock);
+#endif
+        _curr_bundle = 0;
 		//rewind(hit_file); 
 		_hit_fac->reset();
 		next_ref_scaff = ref_mRNAs.begin(); 
@@ -216,6 +256,9 @@ public:
     // samples will clobber each other
     void set_ref_rnas(const vector<shared_ptr<Scaffold> >& mRNAs)
     {
+#if ENABLE_THREADS
+        boost::mutex::scoped_lock lock(_factory_lock);
+#endif
         ref_mRNAs.clear();
         for (vector<shared_ptr<Scaffold> >::const_iterator i = mRNAs.begin(); i < mRNAs.end(); ++i)
         {
@@ -233,11 +276,13 @@ public:
         }
         
         next_ref_scaff = ref_mRNAs.begin();
-        _ref_driven = true;
     }
     
     void set_mask_rnas(const vector<shared_ptr<Scaffold> >& masks)
     {
+#if ENABLE_THREADS
+        boost::mutex::scoped_lock lock(_factory_lock);
+#endif
         mask_gtf_recs = masks;
         RefID last_id = 0;
         for (vector<shared_ptr<Scaffold> >::iterator i = mask_gtf_recs.begin(); i < mask_gtf_recs.end(); ++i)
@@ -254,11 +299,17 @@ public:
         
 	void bad_intron_table(const BadIntronTable& bad_introns) 
 	{ 
+#if ENABLE_THREADS
+        boost::mutex::scoped_lock lock(_factory_lock);
+#endif
 		_bad_introns = bad_introns;
 	}
     
     void read_group_properties(shared_ptr<ReadGroupProperties> rg)
     {
+#if ENABLE_THREADS
+        boost::mutex::scoped_lock lock(_factory_lock);
+#endif
         _rg_props = rg;
     }
     
@@ -270,6 +321,10 @@ public:
 	bool spans_bad_intron(const ReadHit& read);
 	
 private:
+	
+	bool _expand_by_hits(HitBundle& bundle);
+	bool _expand_by_refs(HitBundle& bundle);
+	
 	shared_ptr<HitFactory> _hit_fac;
     
 	vector<shared_ptr<Scaffold> > ref_mRNAs;
@@ -286,11 +341,22 @@ private:
     
     shared_ptr<ReadGroupProperties> _rg_props;
     
-    const ReadHit* next_valid_alignment();
-    bool _ref_driven;
+	// Sets nva to point to the next valid alignment
+	// Returns the mass of any alignments that are seen, valid or not
+    double next_valid_alignment(const ReadHit*& nva);
+	
+	// Backs up the factory to before the last valid alignment
+	// and returns the mass of that alignment (rh)
+	double rewind_hit(const ReadHit* rh);
+
+    BundleMode _bundle_mode;
     int _prev_pos;
     RefID _prev_ref_id;
     int _num_bundles;
+    int _curr_bundle;
+#if ENABLE_THREADS    
+    boost::mutex _factory_lock;
+#endif
 };
 
 void identify_bad_splices(const HitBundle& bundle, 
@@ -298,17 +364,19 @@ void identify_bad_splices(const HitBundle& bundle,
 
 template<class BundleFactoryType>
 void inspect_map(BundleFactoryType& bundle_factory,
-                 long double& map_mass, 
                  BadIntronTable* bad_introns,
-                 EmpDist& frag_len_dist,
+                 vector<pair<string, double> >& count_table,
                  bool progress_bar = true)
 {
 
 	ProgressBar p_bar;
 	if (progress_bar)
 		p_bar = ProgressBar("Inspecting reads and determining fragment length distribution.",bundle_factory.ref_table().size());
-	char last_chrom[100];
-	map_mass = 0.0;
+	RefID last_chrom = 0;
+
+	long double map_mass = 0.0;
+    long double norm_map_mass = 0.0;
+	
 	int min_len = numeric_limits<int>::max();
 	int max_len = def_max_frag_len;
 	vector<double> frag_len_hist(def_max_frag_len+1,0);
@@ -318,35 +386,79 @@ void inspect_map(BundleFactoryType& bundle_factory,
 	size_t total_hits = 0;
 	size_t total_non_redundant_hits = 0;
 	
-	vector<long double> mass_dist; //To be used for quartile normalization
+	//To be used for quartile normalization
+	vector<long double> mass_dist; 	
+	
+	// Store the maximum read length for "left" and "right" reads to report to user.
+	int max_left = 0;
+	int max_right = 0;
+	
+	shared_ptr<MultiReadTable> mrt(new MultiReadTable());
 	
 	while(true)
 	{
 		HitBundle* bundle_ptr = new HitBundle();
 		
-		if (!bundle_factory.next_bundle(*bundle_ptr))
+		bool valid_bundle = bundle_factory.next_bundle(*bundle_ptr);
+		HitBundle& bundle = *bundle_ptr;
+
+        if (use_compat_mass) //only count hits that are compatible with ref transcripts
+        {
+            // Take raw mass even if bundle is "empty", since we could be out of refs
+            // with remaining hits
+            map_mass += bundle.compatible_mass();
+            if (use_quartile_norm && bundle.compatible_mass() > 0) 
+            {
+                mass_dist.push_back(bundle.compatible_mass());
+            }
+        }
+        else if (use_total_mass) //use all raw mass
+        { 
+            
+            // Take raw mass even if bundle is "empty", since we could be out of refs
+            // with remaining hits
+            map_mass += bundle.raw_mass();
+            if (use_quartile_norm && bundle.raw_mass() > 0) 
+            {
+                mass_dist.push_back(bundle.raw_mass());
+            }
+        }
+        else
+        {
+            fprintf(stderr, "Error: hit counting scheme for normalization is not set!\n");
+            assert(false);
+            exit(1);
+        }
+		
+		const RefSequenceTable& rt = bundle_factory.ref_table();
+		const char* chrom = rt.get_name(bundle.ref_id());
+		char bundle_label_buf[2048];
+        if (chrom)
+        {
+            sprintf(bundle_label_buf, "%s:%d-%d", chrom, bundle.left(), bundle.right());
+            verbose_msg("Inspecting bundle %s with %lu reads\n", bundle_label_buf, bundle.hits().size());
+            count_table.push_back(make_pair(bundle_label_buf, bundle.raw_mass()));
+		}
+        
+        if (!valid_bundle)
 		{
 			delete bundle_ptr;
 			break;
 		}
 		num_bundles++;
-		HitBundle& bundle = *bundle_ptr;
-		const RefSequenceTable& rt = bundle_factory.ref_table();
-		const char* chrom = rt.get_name(bundle.ref_id());	
-		char bundle_label_buf[2048];
-		sprintf(bundle_label_buf, "%s:%d-%d", chrom, bundle.left(), bundle.right());
-		verbose_msg("Inspecting bundle %s with %lu reads\n", bundle_label_buf, bundle.hits().size());
-		if (progress_bar) {
-			int inc_amt = (strncmp(last_chrom, chrom, 100)==0) ? 0 : 1;
+        
+        if (progress_bar) 
+        {
+			double inc_amt = last_chrom == bundle.ref_id() ? 0.0 : 1.0;
 			p_bar.update(bundle_label_buf, inc_amt);
-			strncpy(last_chrom, chrom, 100);
-			}
-		
-		if (bad_introns != NULL)
+			last_chrom = bundle.ref_id();
+        }
+        
+        if (bad_introns != NULL)
 		{
 			identify_bad_splices(bundle, *bad_introns);
 		}
-        
+		
 		const vector<MateHit>& hits = bundle.non_redundant_hits();
 		if (hits.empty())
 		{
@@ -359,24 +471,41 @@ void inspect_map(BundleFactoryType& bundle_factory,
 		int curr_range_end = numeric_limits<int>::max();
 		int next_range_start = -1;
 		
-		
 		total_non_redundant_hits += bundle.non_redundant_hits().size();
 		total_hits += bundle.hits().size();
-
+		
 		// This first loop calclates the map mass and finds ranges with no introns
 		// Note that we are actually looking at non-redundant hits, which is why we use collapse_mass
-		double bundle_mass = 0;
+		// This loop will also add multi-reads to the MultiReads table 
 		for (size_t i = 0; i < hits.size(); ++i) 
 		{
-			bundle_mass += hits[i].collapse_mass();
 			assert(hits[i].left_alignment());
-			min_len = min(min_len, hits[i].left_alignment()->right()-hits[i].left_alignment()->left());
+            
+            // Add to table if multi-read
+			if (hits[i].is_multi())
+			{
+				mrt->add_hit(hits[i]);
+			}
+			
+			// Find left length
+			int left_len = hits[i].left_alignment()->right()-hits[i].left_alignment()->left();
+			min_len = min(min_len, left_len);
+			if (!hits[i].left_alignment()->contains_splice())
+				max_left = max(max_left, left_len);
+			
+			// Find right length
 			if (hits[i].right_alignment())
 			{
-				min_len = min(min_len, hits[i].right_alignment()->right()-hits[i].right_alignment()->left());
+				int right_len = hits[i].right_alignment()->right()-hits[i].right_alignment()->left();
+				min_len = min(min_len, right_len);
+				if (!hits[i].right_alignment()->contains_splice())
+					max_right = max(max_right, right_len);
 				has_pairs = true;
 			}
-			if (bundle.ref_scaffolds().size()==1 && hits[i].is_pair()) // Annotation provided and single isoform gene.
+			
+			// Find fragment length
+			if (bundle.ref_scaffolds().size()==1 && hits[i].is_pair())
+			// Annotation provided and single isoform gene
 			{
 				int start, end, mate_length;
 				shared_ptr<Scaffold> scaff = bundle.ref_scaffolds()[0];
@@ -386,7 +515,8 @@ void inspect_map(BundleFactoryType& bundle_factory,
 						frag_len_hist[mate_length] += hits[i].collapse_mass();
 				}
 			}
-			else
+			else if (bundle.ref_scaffolds().empty())
+			// No annotation provided.  Look for ranges.
 			{
 				if (hits[i].left() > curr_range_end)
 				{
@@ -403,7 +533,7 @@ void inspect_map(BundleFactoryType& bundle_factory,
 				}
 				if (hits[i].right_alignment() && hits[i].right_alignment()->contains_splice())
 				{
-					assert(hits[i].right_alignment()->left() > hits[i].left());
+					assert(hits[i].right_alignment()->left() >= hits[i].left());
 					curr_range_end = min(curr_range_end, hits[i].right_alignment()->left()-1);
 					next_range_start = max(next_range_start, hits[i].right());
 				}
@@ -436,25 +566,17 @@ void inspect_map(BundleFactoryType& bundle_factory,
 			}
 		}
 		
-		map_mass += bundle_mass;
-
-		if (use_quartile_norm && bundle_mass > 0) mass_dist.push_back(bundle_mass);
-
-		foreach(shared_ptr<Scaffold>& ref_scaff, bundle.ref_scaffolds())
-		{
-			ref_scaff->clear_hits();
-		}
         open_ranges.clear();
-        
 		delete bundle_ptr;
 	}
 	
-	if (use_quartile_norm)
+    norm_map_mass = map_mass;
+    
+	if (use_quartile_norm && mass_dist.size() > 0)
 	{
 		sort(mass_dist.begin(),mass_dist.end());
-		int num_included = mass_dist.size() * 0.75;
-		map_mass = accumulate(mass_dist.begin(), mass_dist.begin()+num_included, 0.0);
-
+		int upper_quart_index = mass_dist.size() * 0.75;
+		norm_map_mass = mass_dist[upper_quart_index];
 	}
 
     if (bad_introns != NULL)
@@ -471,22 +593,30 @@ void inspect_map(BundleFactoryType& bundle_factory,
             num_introns += itr->second.size();
         }
         
-        asm_verbose( "Bad intron table has %lu introns: (%lu alloc'd, %lu used)\n", num_introns, alloced, used);
-    	asm_verbose( "Map has %lu hits, %lu are non-redundant\n", total_hits, total_non_redundant_hits);
+        verbose_msg( "Bad intron table has %lu introns: (%lu alloc'd, %lu used)\n", num_introns, alloced, used);
+    	verbose_msg( "Map has %lu hits, %lu are non-redundant\n", total_hits, total_non_redundant_hits);
     } 
     
-    long double tot_count = 0;
+	if (progress_bar)
+		p_bar.complete();
+	
 	vector<double> frag_len_pdf(max_len+1, 0.0);
 	vector<double> frag_len_cdf(max_len+1, 0.0);
-        
-    tot_count = accumulate(frag_len_hist.begin(), frag_len_hist.end(), 0.0 );
-    
-    string distr_type;
-	// Calculate the max frag length and interpolate all zeros between min read len and max frag len
+    long double tot_count = accumulate(frag_len_hist.begin(), frag_len_hist.end(), 0.0 );
+    bool empirical = false;
+	
+	if (user_provided_fld && has_pairs && tot_count >= 10000)
+	{
+		fprintf(stderr, "Warning: Overriding empirical fragment length distribution with user-specified parameters is not recommended.\n");
+	}
+	
 	if (!has_pairs || tot_count < 10000)
 	{
+		if (!user_provided_fld)
+		{
+			fprintf(stderr, "Warning: Using default Gaussian distribution due to insufficient paired-end reads in open ranges.  It is recommended that correct paramaters (--frag-len-mean and --frag-len-std-dev) be provided.\n");
+		}
 		tot_count = 0;
-		distr_type  = "Gaussian (default)";
 		normal frag_len_norm(def_frag_len_mean, def_frag_len_std_dev);
 		max_len = def_frag_len_mean + 3*def_frag_len_std_dev;
 		for(int i = min_len; i <= max_len; i++)
@@ -495,9 +625,10 @@ void inspect_map(BundleFactoryType& bundle_factory,
 			tot_count += frag_len_hist[i];
 		}
 	}
-	else 
+	else
+	// Calculate the max frag length and interpolate all zeros between min read len and max frag len
 	{	
-		distr_type = "Empirical (learned)";
+		empirical = true;
 		double curr_total = 0;
 		size_t last_nonzero = min_len-1;
 		for(size_t i = last_nonzero+1; i < frag_len_hist.size(); i++)
@@ -530,20 +661,21 @@ void inspect_map(BundleFactoryType& bundle_factory,
 	}
 	
     double mean = 0.0;
-    
-#if ADAM_MODE    
-    FILE* fhist = fopen(string(output_dir + "/frag_len_hist.csv").c_str(),"w");
-    fprintf(fhist, "Length,Count\n");
-	for(int i = 1; i < frag_len_hist.size(); i++)
-	{
-		fprintf(fhist, "%d,%f\n", i, frag_len_hist[i]);
-	}
-	fclose(fhist);
-#endif	
+
+    if (output_fld)
+    {
+        FILE* fhist = fopen(string(output_dir + "/frag_len_hist.csv").c_str(),"w");
+        fprintf(fhist, "Length,Count\n");
+        for(size_t i = 1; i < frag_len_hist.size(); i++)
+        {
+            fprintf(fhist, "%zu,%f\n", i, frag_len_hist[i]);
+        }
+        fclose(fhist);
+    }
 
 	// Convert histogram to pdf and cdf, calculate mean
 	int frag_len_mode = 0;
-	for(size_t i = min_len; i <= max_len; i++)
+	for(size_t i = min_len; i <= (size_t)max_len; i++)
 	{
 		frag_len_pdf[i] = frag_len_hist[i]/tot_count;
 		frag_len_cdf[i] = frag_len_cdf[i-1] + frag_len_pdf[i];
@@ -561,26 +693,41 @@ void inspect_map(BundleFactoryType& bundle_factory,
     
     std_dev = sqrt(std_dev);
 	
-	frag_len_dist.pdf(frag_len_pdf);
-	frag_len_dist.cdf(frag_len_cdf);
-	frag_len_dist.mode(frag_len_mode);
-	frag_len_dist.max(max_len);
-	frag_len_dist.min(min_len);
-	frag_len_dist.mean(mean);
-	frag_len_dist.std_dev(std_dev);
-	if (progress_bar) {
-			p_bar.complete();
-			fprintf(stderr, "> Map Properties:\n");
-			if (use_quartile_norm)
-				fprintf(stderr, ">\tUpper Quartile Mass: %.2Lf\n", map_mass);
-			else
-				fprintf(stderr, ">\tTotal Map Mass: %.2Lf\n", map_mass);
-			string type = (has_pairs) ? "paired-end" : "single-end";
-			fprintf(stderr, ">\tRead Type: %dbp %s\n", min_len, type.c_str());
-			fprintf(stderr, ">\tFragment Length Distribution: %s\n", distr_type.c_str());
-			fprintf(stderr, ">\t              Estimated Mean: %.2f\n", mean);
-			fprintf(stderr, ">\t           Estimated Std Dev: %.2f\n", std_dev);
-		}
+	shared_ptr<ReadGroupProperties> rg_props = bundle_factory.read_group_properties();
+	shared_ptr<EmpDist const> fld(new EmpDist(frag_len_pdf, frag_len_cdf, frag_len_mode, mean, std_dev, min_len, max_len));
+	rg_props->multi_read_table(mrt);
+	rg_props->frag_len_dist(fld);
+	rg_props->normalized_map_mass(norm_map_mass);
+    rg_props->total_map_mass(map_mass);
+
+	fprintf(stderr, "> Map Properties:\n");
+	if (use_quartile_norm)
+		fprintf(stderr, ">\tUpper Quartile: %.2Lf\n", norm_map_mass);
+	else
+		fprintf(stderr, ">\tTotal Map Mass: %.2Lf\n", norm_map_mass);
+	if (corr_multi)
+		fprintf(stderr,">\tNumber of Multi-Reads: %zu (with %zu total hits)\n", mrt->num_multireads(), mrt->num_multihits()); 
+	if (has_pairs)
+		fprintf(stderr, ">\tRead Type: %dbp x %dbp\n", max_left, max_right);
+	else
+		fprintf(stderr, ">\tRead Type: %dbp single-end\n", max_left);
+
+	if (empirical)
+	{
+		fprintf(stderr, ">\tFragment Length Distribution: Empirical (learned)\n");
+		fprintf(stderr, ">\t              Estimated Mean: %.2f\n", mean);
+		fprintf(stderr, ">\t           Estimated Std Dev: %.2f\n", std_dev);
+	}
+	else
+	{
+		if (user_provided_fld)
+			fprintf(stderr, ">\tFragment Length Distribution: Truncated Gaussian (user-specified)\n");
+		else
+			fprintf(stderr, ">\tFragment Length Distribution: Truncated Gaussian (default)\n");
+		fprintf(stderr, ">\t              Default Mean: %d\n", def_frag_len_mean);
+		fprintf(stderr, ">\t           Default Std Dev: %d\n", def_frag_len_std_dev);
+	}
+
 	bundle_factory.num_bundles(num_bundles);
 	bundle_factory.reset(); 
 	return;
diff --git a/src/clustering.cpp b/src/clustering.cpp
index 7b84ce6..73d1558 100644
--- a/src/clustering.cpp
+++ b/src/clustering.cpp
@@ -22,27 +22,14 @@ void ConnectByExonOverlap::operator()(const AbundanceGroup& cluster,
 	{
 		shared_ptr<Scaffold> scaff_i = abundances[i]->transfrag();
 		assert (scaff_i);
-		const vector<AugmentedCuffOp>& i_ops = scaff_i->augmented_ops();
 		
 		for (size_t j = i + 1; j < abundances.size(); ++j)
 		{
 			shared_ptr<Scaffold> scaff_j = abundances[j]->transfrag();
 			assert (scaff_j);
 			
-			const vector<AugmentedCuffOp>& j_ops = scaff_j->augmented_ops();
-			for (size_t K = 0; K < i_ops.size(); K++)
-			{
-				for (size_t L = 0; L < j_ops.size(); L++)
-				{
-					if (AugmentedCuffOp::overlap_in_genome(i_ops[K], j_ops[L]) &&
-						i_ops[K].opcode == CUFF_MATCH &&
-						j_ops[L].opcode == CUFF_MATCH)
-					{
-						add_edge(i, j, G);
-						break;
-					}
-				}
-			}
+			if (Scaffold::exons_overlap(*scaff_i, *scaff_j))
+				add_edge(i, j, G);
 		}
 	}
 }
diff --git a/src/common.cpp b/src/common.cpp
index 0d77207..7c902dc 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -25,76 +25,85 @@
 
 #include "getopt.h"
 #include "common.h"
+#include "replicates.h"
 
 using namespace std;
 
-bool final_est_run = true;
-bool corr_bias = false;
-bool ref_driven = false;
-
-//int insert_len = 250;
-//int insert_len_std_dev = 20;
-//
-//uint32_t min_anchor_len = 5;
-uint32_t min_intron_length = 50;
-uint32_t max_intron_length = 300000;
-//uint32_t min_exon_length = 100; 
-
-uint32_t max_gene_length = 3500000;
-int max_partner_dist = 50000;
-int def_frag_len_mean = 200;
-int def_frag_len_std_dev = 80;
-int def_max_frag_len = 800;
-int max_frag_len = 800;
-int min_frag_len = 1;
-int olap_radius = 50;
-
-int bowtie_overhang_tolerance = 8; // Typically don't need to change this, except in special cases, such as meta-assembly.
-
-float min_isoform_fraction = 0.1;
-//float min_isoform_fraction = 0.1;
-float pre_mrna_fraction = 0.25;
-float high_phred_err_prob = 0.50; // about MAPQ = 3
 
-double transcript_score_thresh = -0.693;
+// Non-option globals
+bool final_est_run = true;
+bool allow_junk_filtering = true;
+bool user_provided_fld = false;
 
+// Behavior options
 int num_threads = 1;
-
-float max_phred_err_prob = 1.0;
-
-std::string user_label = "CUFF";
-std::string ref_gtf_filename = "";
-std::string mask_gtf_filename = "";
-std::string output_dir = "./";
-std::string fasta_dir;
-std::string bias_mode = "vlmm";
-
-int microexon_length = 25;
-
-bool perform_full_collapse = true;
-
+bool no_update_check = false;
+bool cuff_quiet = false;
 #if ASM_VERBOSE
 bool cuff_verbose = true;
 #else
 bool cuff_verbose = false;
 #endif
+bool output_fld = false;
+bool output_bias_params = false;
 
-bool cuff_quiet = false;
+// General options
+BundleMode bundle_mode = HIT_DRIVEN;
+BundleMode init_bundle_mode = HIT_DRIVEN;
+int max_partner_dist = 50000;
+uint32_t max_gene_length = 3500000;
+std::string ref_gtf_filename = "";
+std::string mask_gtf_filename = "";
+std::string output_dir = "./";
+std::string fasta_dir;
+string default_library_type = "fr-unstranded";
+string library_type = default_library_type;
 
-bool allow_junk_filtering = true;
 
+// Abundance estimation options
+bool corr_bias = false;
+bool corr_multi = false;
 bool use_quartile_norm = false;
-
+bool poisson_dispersion = false;
+BiasMode bias_mode = POS_VLMM;
+int def_frag_len_mean = 200;
+int def_frag_len_std_dev = 80;
+int def_max_frag_len = 800;
+int max_frag_len = 800;
+int min_frag_len = 1;
+float min_isoform_fraction = 0.1;
 int max_mle_iterations = 5000;
 int num_importance_samples = 1000;
+bool use_compat_mass = false;
+bool use_total_mass = false;
+
+
+// Ref-guided assembly options
+int overhang_3 = 600;
+int ref_merge_overhang_tolerance = 30;
+int tile_len = 405;
+int tile_off = 15;
+bool enable_faux_reads = true;
 
+// Assembly options
+uint32_t min_intron_length = 50;
+uint32_t max_intron_length = 300000;
+int olap_radius = 50;
+int bowtie_overhang_tolerance = 8; // Typically don't need to change this, except in special cases, such as meta-assembly.
+int min_frags_per_transfrag = 10;
+int microexon_length = 25;
+float pre_mrna_fraction = 0.15;
+float high_phred_err_prob = 0.50; // about MAPQ = 3
 double small_anchor_fraction = 7 / 75.0;
 double binomial_junc_filter_alpha = 0.001;
+double trim_3_dropoff_frac = .1;
+double trim_3_avgcov_thresh = 10.0;
+std::string user_label = "CUFF";
 
-string default_library_type = "fr-unstranded";
-string library_type = default_library_type;
+bool use_em = true;
+bool cond_prob_collapse = true;
 
-int min_frags_per_transfrag = 10;
+bool emit_count_tables = false;
 
 map<string, ReadGroupProperties> library_type_table;
 const ReadGroupProperties* global_read_properties = NULL;
@@ -105,6 +114,8 @@ boost::thread_specific_ptr<std::string> bundle_label;
 boost::shared_ptr<std::string> bundle_label;
 #endif
 
+long random_seed = -1;
+
 extern void print_usage();
 
 bool gaurd_assembly()
@@ -229,7 +240,7 @@ out:
     free(path);
     return (rv);
 }
-    
+
 void init_library_table()
 {
     ReadGroupProperties fr_unstranded;
@@ -281,11 +292,12 @@ void init_library_table()
     library_type_table["ff-secondstrand"] = ff_secondstrand;
     
     ReadGroupProperties transfrags;
-    fr_unstranded.platform(UNKNOWN_PLATFORM);
-	fr_unstranded.mate_strand_mapping(FR);
-    fr_unstranded.std_mate_orientation(MATES_POINT_TOWARD);
-    fr_unstranded.strandedness(UNSTRANDED_PROTOCOL);
-	
+    transfrags.platform(UNKNOWN_PLATFORM);
+	transfrags.mate_strand_mapping(FR);
+    transfrags.std_mate_orientation(MATES_POINT_TOWARD);
+    transfrags.strandedness(UNSTRANDED_PROTOCOL);
+	transfrags.complete_fragments(true);
+    
     library_type_table["transfrags"] = transfrags;
 	
     //global_read_properties = &(library_type_table.find(default_library_type)->second);
@@ -293,7 +305,7 @@ void init_library_table()
 
 void print_library_table()
 {
-    fprintf (stderr, "Supported library types:\n");
+    fprintf (stderr, "\nSupported library types:\n");
     for (map<string, ReadGroupProperties>::const_iterator itr = library_type_table.begin();
          itr != library_type_table.end();
          ++itr)
@@ -314,7 +326,7 @@ void print_library_table()
 void encode_seq(const string seqStr, char* seq, char* c_seq)
 {
     
-	for (int i = 0; i < seqStr.length(); ++i)
+	for (size_t i = 0; i < seqStr.length(); ++i)
 	{
 		switch(seqStr[i])
 		{
@@ -331,3 +343,15 @@ void encode_seq(const string seqStr, char* seq, char* c_seq)
 	}
 }
 
+
+ReadGroupProperties::ReadGroupProperties() : 
+    _strandedness(UNKNOWN_STRANDEDNESS), 
+    _std_mate_orient(UNKNOWN_MATE_ORIENTATION),
+    _platform(UNKNOWN_PLATFORM),
+    _total_map_mass(0.0),
+    _norm_map_mass(0.0),
+    _mass_scaling_factor(1.0),
+    _complete_fragments(false)
+{
+    _mass_dispersion_model = boost::shared_ptr<MassDispersionModel const>(new PoissonDispersionModel);
+} 
diff --git a/src/common.h b/src/common.h
index e7b954a..73574dd 100644
--- a/src/common.h
+++ b/src/common.h
@@ -9,8 +9,6 @@
  *
  */
 
-using namespace std;
-
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
@@ -18,6 +16,7 @@ using namespace std;
 #include <stdint.h>
 #include <cassert>
 #include <string>
+#include <utility>
 
 #include <boost/math/distributions/normal.hpp> 
 using boost::math::normal;
@@ -27,65 +26,72 @@ using boost::math::normal;
 #define reverse_foreach BOOST_REVERSE_FOREACH
 
 #include <boost/thread.hpp>
-
 #include <boost/shared_ptr.hpp>
 
+// Non-option globals
 extern bool final_est_run;
-extern bool corr_bias;
-extern bool ref_driven;
-
-extern uint32_t max_intron_length;
-extern uint32_t min_intron_length;
-
-extern uint32_t max_gene_length;
-
-extern int max_partner_dist;
-extern int def_frag_len_mean;
-extern int def_frag_len_std_dev;
+extern bool allow_junk_filtering;
+extern bool user_provided_fld;
 extern int def_max_frag_len;
 extern int max_frag_len;
 extern int min_frag_len;
 
-extern double transcript_score_thresh;
-extern int olap_radius;
-extern float pre_mrna_fraction;
-
+// Behavior options
 extern int num_threads;
+extern bool no_update_check;
+extern bool cuff_quiet;
+extern bool cuff_verbose;
+extern bool output_fld;
+extern bool output_bias_params;
 
-extern int bowtie_overhang_tolerance;
-extern float min_isoform_fraction;
-//extern float min_isoform_fraction;
-extern float max_phred_err_prob;
-extern float high_phred_err_prob;
-
-extern std::string user_label;
+// General options
+extern int max_partner_dist;
+extern uint32_t max_gene_length;
 extern std::string ref_gtf_filename;
 extern std::string mask_gtf_filename;
 extern std::string output_dir;
 extern std::string fasta_dir;
-extern std::string bias_mode;
-
-extern int microexon_length;
-extern bool cuff_verbose;
-extern bool cuff_quiet;
-extern bool perform_full_collapse;
-
-extern bool allow_junk_filtering;
+extern std::string library_type;
 
+// Abundance estimation options
+extern bool corr_bias;
+extern bool corr_multi;
 extern bool use_quartile_norm;
-
+extern bool poisson_dispersion;
+extern int def_frag_len_mean;
+extern int def_frag_len_std_dev;
 extern int max_mle_iterations;
 extern int num_importance_samples;
-
+extern float min_isoform_fraction;
+extern bool use_em;
+extern bool cond_prob_collapse;
+extern bool use_compat_mass;
+extern bool use_total_mass;
+
+// Ref-guided assembly options
+extern int overhang_3;
+extern int ref_merge_overhang_tolerance;
+extern int tile_len;
+extern int tile_off;
+extern bool enable_faux_reads;
+
+// Assembly options
+extern uint32_t min_intron_length;
+extern uint32_t max_intron_length;
+extern int olap_radius;
+extern int bowtie_overhang_tolerance;
+extern int min_frags_per_transfrag;
+extern int microexon_length;
+extern float pre_mrna_fraction;
+extern float high_phred_err_prob;
+extern double trim_3_dropoff_frac;
+extern double trim_3_avgcov_thresh;
 extern double small_anchor_fraction;
-
 extern double binomial_junc_filter_alpha;
+extern std::string user_label;
+extern long random_seed;
+extern bool emit_count_tables;
 
-extern std::string library_type;
-
-extern int min_frags_per_transfrag;
-
-#define ADAM_MODE 0
 #define ASM_VERBOSE 0
 #define ENABLE_THREADS 1
 
@@ -130,6 +136,25 @@ OutputIterator copy_if(InputIterator begin,
 	return destBegin;
 }
 
+enum BundleMode
+{
+	HIT_DRIVEN,
+	REF_DRIVEN,
+	REF_GUIDED
+};
+extern BundleMode bundle_mode;
+extern BundleMode init_bundle_mode;
+
+enum BiasMode
+{
+	SITE,
+	VLMM,
+	POS,
+	POS_VLMM,
+    POS_SITE
+};
+extern BiasMode bias_mode;
+
 enum Strandedness 
 {
     UNKNOWN_STRANDEDNESS,
@@ -164,17 +189,19 @@ enum Platform
 class EmpDist
 {
 	//Vectors only valid between min and max!
-	vector<double> _pdf;
-	vector<double> _cdf;
+	std::vector<double> _pdf;
+	std::vector<double> _cdf;
 	int _mode;
-	int _max;
-	int _min;
-    double _mean;
+	double _mean;
     double _std_dev;
+	int _min;
+	int _max;
 	
 public:
+	EmpDist(std::vector<double>& pdf, std::vector<double>& cdf, int mode, double mean, double std_dev, int min, int max)
+	: _pdf(pdf), _cdf(cdf), _mode(mode), _mean(mean), _std_dev(std_dev), _min(min), _max(max) {}
 	
-	void pdf(vector<double>& pdf)	{ _pdf = pdf; }
+	void pdf(std::vector<double>& pdf)	{ _pdf = pdf; }
 	double pdf(int l) const
 	{
 		if (!valid_len(l))
@@ -194,7 +221,7 @@ public:
 		return pdf(l)/cdf(r);
 	}
 	
-	void cdf(vector<double>& cdf)	{ _cdf = cdf; }
+	void cdf(std::vector<double>& cdf)	{ _cdf = cdf; }
 	double cdf(int l) const
 	{
 		if (l > _max)
@@ -224,16 +251,16 @@ public:
 };
 
 class BiasLearner;
+class MultiReadTable;
+
+class MassDispersionModel;
+
 
 class ReadGroupProperties
 {
 public:
     
-    ReadGroupProperties() : 
-    _strandedness(UNKNOWN_STRANDEDNESS), 
-    _std_mate_orient(UNKNOWN_MATE_ORIENTATION),
-    _platform(UNKNOWN_PLATFORM),
-    _total_map_mass(0.0) {} 
+    ReadGroupProperties(); 
     
     Strandedness strandedness() const { return _strandedness; }
     void strandedness(Strandedness s) { _strandedness = s; }
@@ -250,12 +277,45 @@ public:
     long double total_map_mass() const { return _total_map_mass; }
     void total_map_mass(long double p)  { _total_map_mass = p; }  
     
+    long double normalized_map_mass() const { return _norm_map_mass; }
+    void normalized_map_mass(long double p)  { _norm_map_mass = p; }  
+    
     boost::shared_ptr<EmpDist const> frag_len_dist() const { return _frag_len_dist; }
     void frag_len_dist(boost::shared_ptr<EmpDist const> p)  { _frag_len_dist = p; }  
     
 	boost::shared_ptr<BiasLearner const> bias_learner() const { return _bias_learner; }
     void bias_learner(boost::shared_ptr<BiasLearner const> bl)  { _bias_learner = bl; } 
 	
+    void mass_scale_factor(double sf) { _mass_scaling_factor = sf; }
+    double mass_scale_factor() const  { return _mass_scaling_factor; }
+    
+    void complete_fragments(bool c)  { _complete_fragments = c; }
+    bool complete_fragments() const { return _complete_fragments; }
+    
+    double scale_mass(double unscaled_mass) const 
+    { 
+        if (_mass_scaling_factor == 0)
+            return unscaled_mass;
+        
+        return unscaled_mass * (1.0 / _mass_scaling_factor);
+    }
+    
+    boost::shared_ptr<const MassDispersionModel> mass_dispersion_model() const 
+    { 
+        return _mass_dispersion_model; 
+    };
+    
+    void mass_dispersion_model(boost::shared_ptr<const MassDispersionModel> nm) 
+    { 
+        _mass_dispersion_model = nm; 
+    }
+    
+    const std::vector<std::pair<std::string, double> >& common_scale_counts() { return _common_scale_counts; }
+    void common_scale_counts(const std::vector<std::pair<std::string, double> >& counts) { _common_scale_counts = counts; }
+    
+	boost::shared_ptr<MultiReadTable> multi_read_table() const {return _multi_read_table; }	
+	void multi_read_table(boost::shared_ptr<MultiReadTable> mrt) { _multi_read_table = mrt;	}
+	
 private:
     
     Strandedness _strandedness;
@@ -263,8 +323,16 @@ private:
 	MateStrandMapping _mate_strand_mapping;
     Platform _platform;
     long double _total_map_mass;
+    long double _norm_map_mass;
     boost::shared_ptr<EmpDist const> _frag_len_dist;
 	boost::shared_ptr<BiasLearner const> _bias_learner;
+	boost::shared_ptr<MultiReadTable> _multi_read_table;
+    
+    double _mass_scaling_factor;
+    boost::shared_ptr<const MassDispersionModel> _mass_dispersion_model;
+    std::vector<std::pair<std::string, double> > _common_scale_counts;
+    
+    bool _complete_fragments;
 };
 
 extern std::map<std::string, ReadGroupProperties> library_type_table;
@@ -274,6 +342,31 @@ extern const ReadGroupProperties* global_read_properties;
 void print_library_table();
 void init_library_table();
 
+
+template<typename T>
+std::string cat_strings(const T& container, const char* delimiter=",")
+{
+    std::string cat;
+	if (container.empty())
+	{
+		cat = "";
+	}
+	else
+	{
+		typename T::const_iterator itr = container.begin();
+		//cat = *(itr);
+		for (; itr != container.end(); itr++)
+		{
+			if (!(*itr).empty()) {
+				if (!cat.empty()) cat += delimiter;
+				cat += *itr; 
+            }
+		}
+	}
+    
+	return cat;
+}
+
 #define OPT_NUM_IMP_SAMPLES         260
 #define OPT_MLE_MAX_ITER            261
 #define OPT_FDR                     262
@@ -283,5 +376,19 @@ void init_library_table();
 #define OPT_MIN_FRAGS_PER_TRANSFRAG 266
 #define OPT_BIAS_MODE               267
 #define OPT_MIN_INTRON_LENGTH       268
-
+#define OPT_3_PRIME_AVGCOV_THRESH	269
+#define OPT_3_PRIME_DROPOFF_FRAC    270
+#define OPT_POISSON_DISPERSION      271
+#define OPT_NO_UPDATE_CHECK         272
+#define OPT_OUTPUT_FLD              273
+#define OPT_OUTPUT_BIAS_PARAMS      274
+#define OPT_USE_EM                  275
+#define OPT_COLLAPSE_COND_PROB      276
+#define OPT_RANDOM_SEED             277
+#define OPT_NO_FAUX_READS           278
+#define OPT_3_OVERHANG_TOLERANCE    279
+#define OPT_INTRON_OVERHANG_TOLERANCE 280
+#define OPT_EMIT_COUNT_TABLES       281
+#define OPT_USE_COMPAT_MASS         282
+#define OPT_USE_TOTAL_MASS          283
 #endif
diff --git a/src/compress_gtf.cpp b/src/compress_gtf.cpp
new file mode 100644
index 0000000..a2cd10a
--- /dev/null
+++ b/src/compress_gtf.cpp
@@ -0,0 +1,427 @@
+/*
+ *  gtf_to_sam.cpp
+ *  Cufflinks
+ *
+ *  Created by Cole Trapnell on 8/1/10.
+ *  Copyright 2009 Cole Trapnell. All rights reserved.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#else
+#define PACKAGE_VERSION "INTERNAL"
+#endif
+
+#include <stdlib.h>
+#include <getopt.h>
+#include <string>
+#include <algorithm>
+
+#include <boost/version.hpp>
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/depth_first_search.hpp>
+#include <boost/graph/visitors.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/connected_components.hpp>
+
+#include "common.h"
+#include "hits.h"
+#include "bundles.h"
+
+#include "gtf_tracking.h"
+#include "scaffolds.h"
+#include "tokenize.h"
+#include "genes.h"
+
+using namespace boost;
+using namespace std;
+
+#if ENABLE_THREADS
+const char *short_options = "r:F";
+#else
+const char *short_options = "r:F";
+#endif
+
+bool raw_fpkm = false;
+bool proj_union = false;
+bool proj_intersection = false;
+
+static struct option long_options[] = {
+{"reference-seq",		required_argument,		 0,			 'r'},
+{"raw-fpkm",            no_argument,             0,			 'F'},
+{"union",               no_argument,             0,			 'U'},
+{"intersection",        no_argument,             0,			 'I'},
+
+
+{0, 0, 0, 0} // terminator
+};
+
+void print_usage()
+{
+	//NOTE: SPACES ONLY, bozo
+	fprintf(stderr, "compress_gtf v%s\n", PACKAGE_VERSION);
+	fprintf(stderr, "linked against Boost version %d\n", BOOST_VERSION);
+	fprintf(stderr, "-----------------------------\n"); 
+	fprintf(stderr, "Usage:   compress_gtf [options] <reference.gtf> <compressed_reference.gtf>\n");
+	fprintf(stderr, "Options:\n\n");
+	fprintf(stderr, "-r/--reference-seq			  reference fasta file                     [ default:   NULL ]\n");
+    fprintf(stderr, "-F/--raw-fpkm			      use FPKM instead of isoform fraction                        \n");
+    fprintf(stderr, "-U/--union                   report projective union                  [ default:   OFF  ]\n");
+    fprintf(stderr, "-I/--intersection            report projective intersection           [ default:   ON   ]\n");
+}
+
+int parse_options(int argc, char** argv)
+{
+    int option_index = 0;
+    int next_option;
+    do {
+        next_option = getopt_long(argc, argv, short_options, long_options, &option_index);
+        switch (next_option) {
+			case -1:     /* Done with options. */
+				break;
+            case 'r':
+			{
+				fasta_dir = optarg;
+				break;
+            }    
+            case 'F':
+			{
+				raw_fpkm = true;
+				break;
+            }   
+            case 'U':
+			{
+				proj_union = true;
+				break;
+            } 
+            case 'I':
+			{
+				proj_intersection = true;
+				break;
+            }  
+			default:
+				print_usage();
+				return 1;
+        }
+    } while(next_option != -1);
+
+    if (proj_union && proj_intersection)
+    {
+        fprintf (stderr, "Error: please specify only one of --union and --intersection");
+        exit(1);
+    }
+    
+//    if (!proj_union && !proj_intersection)
+//        proj_intersection =  true;
+	return 0;
+}
+
+void compress_genes(FILE* ftranscripts,
+                    RefSequenceTable& rt,
+                    vector<shared_ptr<Scaffold> >& ref_mRNAs)
+{
+    adjacency_list <vecS, vecS, undirectedS> G;
+    
+	for (size_t i = 0; i < ref_mRNAs.size(); ++i)
+	{
+		add_vertex(G);
+	}
+	
+    for (size_t i = 0; i < ref_mRNAs.size(); ++i)
+	{
+        shared_ptr<Scaffold> scaff_i = ref_mRNAs[i];
+        for (size_t j = 0; j < ref_mRNAs.size(); ++j)
+        {
+            shared_ptr<Scaffold> scaff_j = ref_mRNAs[j];
+			if (scaff_i->annotated_gene_id() == scaff_j->annotated_gene_id())
+				add_edge(i, j, G);
+		}
+	}
+    
+    std::vector<int> component(num_vertices(G));
+	connected_components(G, &component[0]);
+	
+	vector<vector<bool> > clusters(ref_mRNAs.size(), 
+								   vector<bool>(ref_mRNAs.size(), false));
+	
+	//vector<vector<size_t> > cluster_indices(three_prime_ends.size());
+    
+    vector<vector<shared_ptr<Scaffold> > > grouped_scaffolds(ref_mRNAs.size());
+	for (size_t i = 0; i < ref_mRNAs.size(); ++i)
+	{
+		clusters[component[i]][i] = true;
+		grouped_scaffolds[component[i]].push_back(ref_mRNAs[i]);
+	}
+    
+    for (size_t i = 0; i < grouped_scaffolds.size(); ++i)
+    {
+        vector<shared_ptr<Scaffold> >& gene = grouped_scaffolds[i];
+        vector<Scaffold> gene_scaffs;
+        string gene_id;
+        foreach (shared_ptr<Scaffold> s, gene)
+        {
+            if (gene_id == "")
+                gene_id = s->annotated_gene_id();
+            
+            gene_scaffs.push_back(*s);
+        }
+        
+        if (gene_scaffs.empty())
+            continue;
+        
+        next_gene_id++;
+        
+        Scaffold smashed_gene;
+        if (!proj_intersection && !proj_union)
+        {
+            foreach (shared_ptr<Scaffold> s, gene)
+            {
+                /*
+                 *transfrag,
+                 gene_id,
+                 (int)isoforms.size() + 1,
+                 FPKM,
+                 iso_ab->effective_length(),
+                 iso_ab->gamma(),
+                 iso_ab->FPKM_conf(),
+                 density_per_bp, 
+                 estimated_count,
+                 density_score,
+                 iso_ab->status(),
+                 ref_gene_id)*/
+                
+                Isoform iso(*s,
+                            -1,
+                            1,
+                            0.0,
+                            s->length(),
+                            0.0,
+                            ConfidenceInterval(0.0,0.0),
+                            0,
+                            0,
+                            0,
+                            NUMERIC_OK,
+                            gene_id);
+                vector<string> isoform_exon_recs;
+                
+                iso.get_gtf(isoform_exon_recs, rt);
+                
+                for (size_t g = 0; g < isoform_exon_recs.size(); ++g)
+                {
+                    fprintf(ftranscripts, "%s", isoform_exon_recs[g].c_str());
+                }
+            }
+        }
+        else
+        {
+            if (proj_union)
+                Scaffold::merge(gene_scaffs, smashed_gene, false);
+            else if (proj_intersection)
+            {
+                vector<AugmentedCuffOp> iso_ops;
+                
+                int gmax = -1;
+                int gmin = numeric_limits<int>::max();
+                
+                foreach (shared_ptr<Scaffold> s, gene)
+                {
+                    //iso_ops.push_back(s->augmented_ops());
+                    //sort (iso_ops.back().begin(), iso_ops.back().end());
+                    if (s->left() < gmin)
+                        gmin = s->left();
+                    if (s->right() > gmax)
+                        gmax = s->right();
+                }
+                
+                foreach (shared_ptr<Scaffold> s, gene)
+                {
+                    if (s->left() > gmin)
+                    {
+                        iso_ops.push_back(AugmentedCuffOp(CUFF_INTRON, gmin, s->left() - gmin)); 
+                    }
+                    if (s->right() < gmax)
+                    {
+                        iso_ops.push_back(AugmentedCuffOp(CUFF_INTRON, s->right(), gmax - s->right())); 
+                    }
+                    iso_ops.insert(iso_ops.end(), s->augmented_ops().begin(), s->augmented_ops().end());
+                }
+//                vector<AugmentedCuffOp> intersect = iso_ops.front();
+//                for (size_t j = 1; j < iso_ops.size(); ++j)
+//                {
+//                    vector<AugmentedCuffOp> tmp;
+//                    const vector<AugmentedCuffOp>& iso_ops_j = iso_ops[j];
+//                    //set_intersection(intersect.begin(), intersect.end(), iso_ops_j.begin(), iso_ops_j.end(), back_inserter(tmp));
+//                    intersect.insert(intersect.end(), iso_ops_j.begin(), iso_ops_j.end());
+//                    
+//                    intersect.push_back(
+//                    assert (tmp.size() <= intersect.size());
+//                    //intersect = tmp;
+//                    //sort(intersect.begin(), intersect.end());
+//                }
+//                
+                sort(iso_ops.begin(), iso_ops.end(), AugmentedCuffOp::g_left_lt);
+//                
+//                while (!intersect.empty() && intersect.front().opcode != CUFF_MATCH)
+//                {
+//                    intersect.erase(intersect.begin());
+//                }
+//                
+//                while (!intersect.empty() && intersect.back().opcode != CUFF_MATCH)
+//                {
+//                    intersect.pop_back();
+//                }
+//                
+//                if (intersect.empty())
+//                    continue;
+                
+                vector<AugmentedCuffOp> merged_ops;
+                AugmentedCuffOp::merge_ops(iso_ops, merged_ops, true, true);
+                vector<AugmentedCuffOp>::iterator first_match = merged_ops.begin();
+                vector<AugmentedCuffOp>::iterator last_match = merged_ops.end();
+                last_match--;
+                while(first_match < merged_ops.end())
+                {
+                    if (first_match->opcode == CUFF_MATCH)
+                        break;
+                    first_match++;
+                }
+                while(last_match >= merged_ops.begin() && last_match< merged_ops.end())
+                {
+                    if (last_match->opcode == CUFF_MATCH)
+                        break;
+                    last_match--;
+                }
+                
+                vector<AugmentedCuffOp> internal_matches;
+                if (last_match >= first_match && last_match < merged_ops.end())
+                {
+                    last_match++;
+                    
+                    internal_matches.insert(internal_matches.end(), first_match, last_match);
+                    smashed_gene = Scaffold(gene.front()->ref_id(), gene.front()->strand(), internal_matches);
+                }
+                else
+                {
+                    
+                    fprintf(stderr, "Could not find consitutive region for %s\n", gene_id.c_str());
+                    continue;
+                }
+                
+            }
+            else
+                assert(false);
+            assert (smashed_gene.ref_id());
+            
+            Isoform iso(smashed_gene,
+                        -1,
+                        1,
+                        0.0,
+                        smashed_gene.length(),
+                        0.0,
+                        ConfidenceInterval(0.0,0.0),
+                        0, 
+                        0,
+                        0,
+                        NUMERIC_OK,
+                        gene_id);
+            vector<string> isoform_exon_recs;
+            
+            iso.get_gtf(isoform_exon_recs, rt);
+            
+            for (size_t g = 0; g < isoform_exon_recs.size(); ++g)
+            {
+                fprintf(ftranscripts, "%s", isoform_exon_recs[g].c_str());
+            }
+        }
+        
+        fflush(ftranscripts);
+    }
+}
+
+void driver(vector<FILE*> ref_gtf_files, FILE* gtf_out)
+{
+	ReadTable it;
+	RefSequenceTable rt(true, false);
+	
+	vector<vector<shared_ptr<Scaffold> > > ref_mRNA_table;
+	vector<pair<string, vector<double> > > sample_count_table;
+    
+    foreach (FILE* ref_gtf, ref_gtf_files)
+    {
+        vector<shared_ptr<Scaffold> > ref_mRNAs;
+        ::load_ref_rnas(ref_gtf, rt, ref_mRNAs, false, true);
+        ref_mRNA_table.push_back(ref_mRNAs);
+    }
+    
+    for (size_t j = 0; j < ref_mRNA_table.size(); ++j)
+    {
+        vector<shared_ptr<Scaffold> > ref_mRNAs = ref_mRNA_table[j];
+        
+        if (!raw_fpkm)
+            compress_genes(gtf_out, rt, ref_mRNAs);
+    }
+}
+
+int main(int argc, char** argv)
+{
+    init_library_table();
+    
+	int parse_ret = parse_options(argc,argv);
+    if (parse_ret)
+        return parse_ret;
+	
+    
+    if(optind >= argc)
+    {
+        print_usage();
+        return 1;
+    }
+	
+    string ref_gtf_in_filenames = argv[optind++];
+    
+    if(optind >= argc)
+    {
+        print_usage();
+        return 1;
+    }
+	
+    string gtf_out_filename = argv[optind++];
+    
+    vector<string> ref_gtf_filenames;
+    tokenize(ref_gtf_in_filenames, ",", ref_gtf_filenames);
+    
+    vector<FILE*> ref_gtf_files;
+    
+    foreach (const string& ref_gtf_in_filename, ref_gtf_filenames)
+    {
+        FILE* ref_gtf = NULL;
+        if (ref_gtf_in_filename != "")
+        {
+            ref_gtf = fopen(ref_gtf_in_filename.c_str(), "r");
+            if (!ref_gtf)
+            {
+                fprintf(stderr, "Error: cannot open GTF file %s for reading\n",
+                        ref_gtf_in_filename.c_str());
+                exit(1);
+            }
+            ref_gtf_files.push_back(ref_gtf);
+        }
+    }
+    
+    FILE* gtf_out = NULL;
+	if (gtf_out_filename != "")
+	{
+		gtf_out = fopen(gtf_out_filename.c_str(), "w");
+		if (!gtf_out)
+		{
+			fprintf(stderr, "Error: cannot open GTF file %s for writing\n",
+					gtf_out_filename.c_str());
+			exit(1);
+		}
+	}
+    
+    driver(ref_gtf_files, gtf_out);
+	
+	return 0;
+}
diff --git a/src/cuffcompare.cpp b/src/cuffcompare.cpp
index 04721ea..380019d 100644
--- a/src/cuffcompare.cpp
+++ b/src/cuffcompare.cpp
@@ -1,7 +1,8 @@
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #else
- #define PACKAGE_VERSION "0.853"
+#define PACKAGE_VERSION "INTERNAL"
+#define SVN_REVISION "SVN"
 #endif
 
 #include "GArgs.h"
@@ -9,24 +10,27 @@
 #include <errno.h>
 #include "gtf_tracking.h"
 
+#ifdef HAVE_CONFIG_H
+#include "update_check.h"
+#endif
+
 #define USAGE "Usage:\n\
 cuffcompare [-r <reference_mrna.gtf>] [-R] [-T] [-V] [-s <seq_path>] \n\
-    [-o <stats.txt>] [-p <cprefix>] \n\
+    [-o <outprefix>] [-p <cprefix>] \n\
     {-i <input_gtf_list> | <input1.gtf> [<input2.gtf> .. <inputN.gtf>]}\n\
 \n\
- Provides classification and various statistics for Cufflinks' transfrags.\n\
- Tracks expression changes of loci across multiple input files, writing\n\
- matching transcripts (intron chains) into <stats>.tracking, and a GTF\n\
- file <stats>.combined.gtf showing a nonredundant set of transcripts \n\
- across all input files, with a single representative transcript selected\n\
- for each set of fully matching transcripts.\n\
+ Cuffcompare provides classification, reference annotation mapping and various\n\
+ statistics for Cufflinks transfrags.\n\
+ Cuffcompare clusters and tracks transfrags across multiple samples, writing\n\
+ matching transcripts (intron chains) into <outprefix>.tracking, and a GTF\n\
+ file <outprefix>.combined.gtf containing a nonredundant set of transcripts \n\
+ across all input files (with a single representative transfrag chosen\n\
+ for each clique of matching transfrags across samples).\n\
 \n\
 Options:\n\
--i provide a file with a list of gtf files to process, instead of giving\n\
-   the files as arguments; use this when a large number of input files\n\
-   should be processed and no individual accuracy statistics is needed\n\
-\n\
--d  max distance (range) for grouping transcript start sites (100)n\
+-i provide a text file with a list of Cufflinks GTF files to process instead\n\
+   of expecting them as command line arguments (useful when a large number\n\
+   of GTF files should be processed)\n\
 \n\
 -r  a set of known mRNAs to use as a reference for assessing \n\
     the accuracy of mRNAs or gene models given in <input.gtf>\n\
@@ -40,20 +44,21 @@ Options:\n\
     a directory containing multiple single-fasta files (one file per contig);\n\
     lower case bases will be used to classify input transcripts as repeats\n\
 \n\
+-d  max distance (range) for grouping transcript start sites (100)\n\
 -p  the name prefix to use for consensus transcripts in the \n\
-    <stats>.combined.gtf file (default: 'TCONS')\n\
-\n\
--T  do not generate .tmap and .refmap files for each query file\n\
-\n\
--V  (mildly) verbose processing mode\n\
+    <outprefix>.combined.gtf file (default: 'TCONS')\n\
+-C  include the \"contained\" transcripts in the .combined.gtf file\n\
+-G  generic GFF input file(s) (do not assume Cufflinks GTF)\n\
+-T  do not generate .tmap and .refmap files for each input file\n\
+-V  verbose processing mode (showing all GFF parsing warnings)\n\
 "
 bool debug=false;
-bool perContigStats=true;
+bool perContigStats=false; // -S to enable stats for every single contig
+bool generic_GFF=false; //-G, don't assume Cufflinks GTF as input
+bool showContained=false; // -C
 bool reduceRefs=false;
 bool checkFasta=false;
 bool tmapFiles=true;
-bool qtracking=true;
-bool debugExit=false;
 bool only_spliced_refs=false;
 int debugCounter=0;
 
@@ -73,9 +78,6 @@ int tssDist=100;
 //int total_tcons=0;
 int total_xloci_alt=0;
 
-#define EQCHAIN_TAG     0x80000
-#define EQCHAIN_TAGMASK 0xFFFFF
-
 void openfwrite(FILE* &f, GArgs& args, char opt) {
   GStr s=args.getOpt(opt);
   if (!s.is_empty()) {
@@ -120,12 +122,12 @@ class GSeqTrack {
 char* getFastaFile(int gseq_id);
 
 // ref globals
-bool haveRefs=false;  //if a reference is given => full metrics generated
+bool haveRefs=false;  //true if a reference annotation (-r) is provide
 
 GList<GSeqData> ref_data(true,true,true); //list of reference mRNAs and loci data for each genomic seq
               //each locus will keep track of any superloci which includes it, formed during the analysis
 
-void processLoci(GSeqData& seqdata, GSeqData* refdata=NULL, GFaSeqGet* faseq=NULL, int qfidx=0);
+void processLoci(GSeqData& seqdata, GSeqData* refdata=NULL, int qfidx=0);
 
 void reportStats(FILE* fout, const char* setname, GSuperLocus& stotal,
        GSeqData* seqdata=NULL, GSeqData* refdata=NULL);
@@ -150,24 +152,31 @@ GList<GStr> qryfiles(false,true,false);
 GList<GSeqTrack> gseqtracks(true,true,true);
 GSeqTrack* findGSeqTrack(int gsid);
 
+
+void show_usage() {
+  GMessage("cuffcompare v%s (%s)\n", PACKAGE_VERSION, SVN_REVISION);
+  GMessage( "-----------------------------\n");
+  GMessage("%s\n", USAGE);
+  }
+
 int main(int argc, char * const argv[]) {
-  GArgs args(argc, argv, "XDTMNVGCKRLhp:c:d:s:i:n:r:o:");
+  GArgs args(argc, argv, "XDTMNVGSCKRLhp:c:d:s:i:n:r:o:");
   int e;
-  if ((e=args.isError())>0)
-    GError("%s\nInvalid argument: %s\n", USAGE, argv[e]);
+  if ((e=args.isError())>0) {
+    show_usage();
+    GMessage("Invalid argument: %s\n", argv[e]);
+    exit(1);
+    }
   if (args.getOpt('h')!=NULL){
-    GMessage("cuffcompare v%s\n", PACKAGE_VERSION); 
-    GMessage("-----------------------------\n"); 
-    GMessage("%s\n", USAGE);
-                exit(1);
-  }
-  debugExit=(args.getOpt('X')!=NULL);
-  debug=(args.getOpt('D')!=NULL || debugExit);
+    show_usage();
+    exit(1);
+    }
+  showContained=(args.getOpt('C')!=NULL);
+  debug=(args.getOpt('D')!=NULL);
   tmapFiles=(args.getOpt('T')==NULL);
-  bool useFastaSeq=(args.getOpt('s')!=NULL);
   multiexon_only=(args.getOpt('M')!=NULL);
   multiexonrefs_only=(args.getOpt('N')!=NULL); 
-  perContigStats=(args.getOpt('G')==NULL);
+  perContigStats=(args.getOpt('S')!=NULL);
   checkFasta=(args.getOpt('K')!=NULL);
   gtf_tracking_verbose=((args.getOpt('V')!=NULL) || debug);
   FILE* finlst=NULL;
@@ -191,7 +200,7 @@ int main(int argc, char * const argv[]) {
             }
           delete lr;
           //if (qryfiles.Count()>10) 
-             gtf_tracking_largeScale=true;
+          gtf_tracking_largeScale=true;
           }
          else {
           numqryfiles=args.startNonOpt();
@@ -205,14 +214,17 @@ int main(int argc, char * const argv[]) {
           }
         numqryfiles=qryfiles.Count();
         if (numqryfiles==0) {
-         GError("cuffcompare v%s\n-----------------------------\n%s\n", PACKAGE_VERSION, USAGE);
-         }
+          show_usage();
+          exit(1);
+          }
       if (numqryfiles>MAX_QFILES) {
            GMessage("Error: too many input files (limit set to %d at compile time)\n",MAX_QFILES);
            GMessage("(if you need to raise this limit set a new value for\nMAX_QFILES in gtf_tracking.h and recompile)\n");
            exit(0x5000);
            }
-
+  #ifdef HAVE_CONFIG_H
+  check_version(PACKAGE_VERSION);
+  #endif
   gfasta.init(args.getOpt('s'));
    // determine if -s points to a multi-fasta file or a directory
   s=args.getOpt('c');
@@ -235,42 +247,59 @@ int main(int argc, char * const argv[]) {
     if (f_ref==NULL) GError("Error opening reference gff: %s\n",s.chars());
     haveRefs=true;
     if (gtf_tracking_verbose) GMessage("Loading reference transcripts..\n");
-    read_mRNAs(f_ref, ref_data, &ref_data, true, -1, s.chars(), useFastaSeq, (multiexonrefs_only || multiexon_only));
+    read_mRNAs(f_ref, ref_data, &ref_data, true, -1, s.chars(), (multiexonrefs_only || multiexon_only));
     haveRefs=(ref_data.Count()>0);
     reduceRefs=(args.getOpt('R')!=NULL);
-    if (gtf_tracking_verbose) GMessage("..ref data loaded\n");
-  }
-
-  s=args.getOpt('o'); //if a full pathname is given
-   //the other common output files will still be created in the current directory
-  if (s.is_empty()) s="stdout";
-  else {
-    int sp=s.rindex(CHPATHSEP);
-    if (sp>=0) s.cut(0,sp+1);
-    //if (di>0) s.cut(di);
-    int di=s.rindex('.');
-    if (di>0 && di>s.length()-5) s.cut(di); // meh, not really needed/wanted?
+    if (gtf_tracking_verbose) GMessage("..reference annotation loaded\n");
+    }
+  bool discard_redundant=true; //discard redundant input transfrags
+  generic_GFF=args.getOpt('G');
+  if (generic_GFF) discard_redundant=false; //generic GTF, don't try to discard "redundant" transcripts
+  //if a full pathname is given
+  //the other common output files will still be created in the current directory:
+  // .loci, .tracking, .stats
+  GStr outbasename; //include path, if provided
+  GStr outprefix; //without path and/or extension
+  GStr outstats=args.getOpt('o');
+  if (outstats.is_empty()) {
+       outstats="cuffcmp";
+       }
+  outbasename=outstats;
+  GStr outext(getFileExt(outstats.chars()));
+  if (outext.is_empty()) {
+    outext="stats";
+    outstats.append(".stats");
+    outbasename=outstats;
     }
-  GStr fbasename=s;
+    else outext.lower();
+  if (outext=="txt" || outext=="out" || outext=="stats" || outext=="summary") {
+      outbasename.cut(outbasename.length()-outext.length()-1);
+      }
+
+  outprefix=outbasename;
+  int di=outprefix.rindex(CHPATHSEP);
+  if (di>=0) outprefix.cut(0,di+1);
+  
   if (debug) { //create a few more files potentially useful for debugging
-             s.append(".missed_introns.gtf");
-             
-             f_mintr=fopen(s.chars(),"w");
-             if (f_mintr==NULL) GError("Error creating file %s!\n",s.chars());
-             
-             /*
-             s=fbasename;
-             s.append(".noTP_introns.gtf");
-             f_nintr=fopen(s.chars(),"w");
-             s=fbasename;
-             s.append(".wrong_Qintrons.gtf");
-             f_qintr=fopen(s.chars(),"w");
-             */
-          }
+        s=outbasename;
+        s.append(".missed_introns.gtf");
+        f_mintr=fopen(s.chars(),"w");
+        if (f_mintr==NULL) GError("Error creating file %s!\n",s.chars());
+        /*
+        s=outbasename;
+        s.append(".noTP_introns.gtf");
+        f_nintr=fopen(s.chars(),"w");
+        s=outbasename;
+        s.append(".wrong_Qintrons.gtf");
+        f_qintr=fopen(s.chars(),"w");
+        */
+        }
 
-  //bool reportLoci=(args.getOpt('L')!=NULL);
-  openfwrite(f_out, args, 'o');
-  if (f_out==NULL) f_out=stdout;
+  //openfwrite(f_out, args, 'o');
+  //if (f_out==NULL) f_out=stdout;
+  f_out=fopen(outstats, "w");
+  if (f_out==NULL) GError("Error creating output file %s!\n", outstats.chars());
+  GMessage("Cuffcompare prefix for output files: %s\n", outprefix.chars());
   fprintf(f_out, "# Cuffcompare v%s | Command line was:\n#", PACKAGE_VERSION);
   for (int i=0;i<argc-1;i++) 
     fprintf(f_out, "%s ", argv[i]);
@@ -279,53 +308,65 @@ int main(int argc, char * const argv[]) {
   GList<GSeqData>** qrysdata=NULL;
   FILE** tfiles=NULL;
   FILE** rtfiles=NULL;
-        GMALLOC(qrysdata, numqryfiles*sizeof(GList<GSeqData>*));
-  if (qtracking && tmapFiles) {
+  GMALLOC(qrysdata, numqryfiles*sizeof(GList<GSeqData>*));
+  if (tmapFiles) {
       GMALLOC(tfiles, numqryfiles*sizeof(FILE*));
-      GMALLOC(rtfiles, numqryfiles*sizeof(FILE*));
+      if (haveRefs) {
+          GMALLOC(rtfiles, numqryfiles*sizeof(FILE*));
+          }
       }
-  //char* infile=NULL;
-        if (gtf_tracking_verbose) GMessage(" Number of query files to process: %d\n",numqryfiles);
   for (int fi=0;fi<qryfiles.Count();fi++) {
-  //while ((infile=args.nextNonOpt())!=NULL) {
-    GStr infname(qryfiles[fi]->chars());
-    if (debug || (gtf_tracking_verbose && !gtf_tracking_largeScale)) GMessage("Processing qfile #%d: %s\n",fi, infname.chars());
-    if (debugExit) continue;
-    if (infname=="-") { f_in=stdin; infname="stdin"; }
+    GStr in_file(qryfiles[fi]->chars());
+    GStr infname(getFileName(qryfiles[fi]->chars())); //file name only
+    GStr indir(qryfiles[fi]->chars());
+    di=indir.rindex(CHPATHSEP);
+    if (di>=0) indir.cut(di+1); //directory path for this input file
+          else indir=""; //current directory
+    
+    if (debug || (gtf_tracking_verbose && !gtf_tracking_largeScale))
+        GMessage("Processing qfile #%d: %s\n",fi+1, in_file.chars());
+    if (in_file=="-") { f_in=stdin; in_file="stdin"; }
       else {
-        f_in=fopen(infname.chars(),"r");
-        if (f_in==NULL) GError("Cannot open input file %s!\n",infname.chars());
-      }
-    //f_in is the opened gff file to process
-    if (qtracking && tmapFiles) {
-        s=infname;
-        int di=s.rindex(CHPATHSEP);
-        if (di>=0) s.cut(0,di+1);
-        GStr sbase(s);
+        f_in=fopen(in_file.chars(),"r");
+        if (f_in==NULL) 
+            GError("Cannot open input file %s!\n",in_file.chars());
+        }
+    //f_in is the query gff file to process
+    
+    GStr sbase(indir);
+    sbase.append(outprefix);
+    sbase.append(".");
+    sbase.append(infname);
+    if (tmapFiles) {
+        //-- we should keep the infname path, otherwise the remaining file names 
+        //   may be the same and clobber each other
+        s=sbase;
         s.append(".tmap");
         tfiles[fi]=fopen(s.chars(),"w");
         if (tfiles[fi]==NULL)
           GError("Error creating file '%s'!\n",s.chars());
-        fprintf(tfiles[fi],"ref_gene_id\tref_id\tclass_code\tcuff_gene_id\tcuff_id\tFMI\tFPKM\tFPKM_conf_lo\tFPKM_conf_hi\tcov\tlen\tmajor_iso_id\n");
-        s=sbase;
-        s.append(".refmap");
-        rtfiles[fi]=fopen(s.chars(),"w");
-        if (rtfiles[fi]==NULL)
-          GError("Error creating file '%s'!\n",s.chars());
-        fprintf(rtfiles[fi],"ref_gene_id\tref_id\tclass_code\tcuff_id_list\n");
-      }
+        fprintf(tfiles[fi],"ref_gene_id\tref_id\tclass_code\tcuff_gene_id\tcuff_id\tFMI\tFPKM\tFPKM_conf_lo\tFPKM_conf_hi\tcov\tlen\tmajor_iso_id\tref_match_len\n");
+        if (haveRefs) {
+          s=sbase;
+          s.append(".refmap");
+          rtfiles[fi]=fopen(s.chars(),"w");
+          if (rtfiles[fi]==NULL)
+             GError("Error creating file '%s'!\n",s.chars());
+          fprintf(rtfiles[fi],"ref_gene_id\tref_id\tclass_code\tcuff_id_list\n");
+          }
+        }
 
       GList<GSeqData>* pdata=new GList<GSeqData>(true,true,true);
       qrysdata[fi]=pdata;
-      if (gtf_tracking_verbose) GMessage("Loading transcripts from %s..\n",infname.chars());
-      read_mRNAs(f_in, *pdata, &ref_data, true, fi, infname.chars(), useFastaSeq, multiexon_only);
+      if (gtf_tracking_verbose) GMessage("Loading transcripts from %s..\n",in_file.chars());
+      read_mRNAs(f_in, *pdata, &ref_data, discard_redundant, fi, in_file.chars(), multiexon_only);
       GSuperLocus gstats;
       GFaSeqGet *faseq=NULL;
       for (int g=0;g<pdata->Count();g++) { //for each seqdata related to a genomic sequence
         int gsid=pdata->Get(g)->gseq_id;
         GSeqData* refdata=getRefData(gsid, ref_data);//ref data for this contig
         if (!gtf_tracking_largeScale)
-          processLoci(*(pdata->Get(g)), refdata, faseq, fi);
+          processLoci(*(pdata->Get(g)), refdata, fi);
         GSeqTrack* seqtrack=findGSeqTrack(gsid); //this will add a gseqtrack if it doesn't exist
         // for gsid
         if (refdata!=NULL) {
@@ -346,25 +387,22 @@ int main(int argc, char * const argv[]) {
           int gsid=refdata->gseq_id;
           if (getQryData(gsid, *pdata)==NULL) {
             reportStats(f_out, getGSeqName(gsid), gstats, NULL, refdata);
-          }//completely missed all refdata on this contig
+            }//completely missed all refdata on this contig
         }
       }
       //now report the summary:
-      if (!gtf_tracking_largeScale) reportStats(f_out, infname.chars(), gstats);
+      if (!gtf_tracking_largeScale) reportStats(f_out, in_file.chars(), gstats);
       if (f_in!=stdin) fclose(f_in);
       //qfileno++;
   }//for each input file
-        if (f_mintr!=NULL) fclose(f_mintr);
-  if (debugExit) exit(0x200);
-  if (qtracking) {
-    if (gtf_tracking_verbose) GMessage("Tracking transcripts across %d query files..\n", numqryfiles);
-    trackGData(numqryfiles, gseqtracks, fbasename, tfiles, rtfiles);
-    fprintf(f_out, "\n Total union super-loci across all input datasets: %d \n", xlocnum);
-    if (numqryfiles>1) {
-        fprintf(f_out, "  (%d multi-transcript, ~%.1f transcripts per locus)\n",
-             total_xloci_alt, ((double)(GXConsensus::count))/xlocnum);
-        }
-    }
+  if (f_mintr!=NULL) fclose(f_mintr);
+  if (gtf_tracking_verbose) GMessage("Tracking transcripts across %d query files..\n", numqryfiles);
+  trackGData(numqryfiles, gseqtracks, outbasename, tfiles, rtfiles);
+  fprintf(f_out, "\n Total union super-loci across all input datasets: %d \n", xlocnum);
+  if (numqryfiles>1) {
+      fprintf(f_out, "  (%d multi-transcript, ~%.1f transcripts per locus)\n",
+           total_xloci_alt, ((double)(GXConsensus::count))/xlocnum);
+      }
   if (gtf_tracking_verbose) GMessage("Cleaning up..\n");
   GFREE(cprefix);
   // clean up
@@ -372,10 +410,8 @@ int main(int argc, char * const argv[]) {
     delete qrysdata[i];
     }
   GFREE(qrysdata);
-  if (qtracking) {
-    GFREE(tfiles);
-    GFREE(rtfiles);
-  }
+  GFREE(tfiles);
+  GFREE(rtfiles);
   gseqtracks.Clear();
   FRCLOSE(f_ref);
   FWCLOSE(f_out);
@@ -399,9 +435,7 @@ bool ichainMatch(GffObj* t, GffObj* r, bool& exonMatch, int fuzz=0) {
   exonMatch=false;
   int imax=r->exons.Count()-1;
   int jmax=t->exons.Count()-1;
-  //single-exon mRNAs ?
-  if (imax==0 || jmax==0) {
-     //only consider match if both are single exon
+  if (imax==0 || jmax==0) {   //single-exon mRNAs
      if (imax!=jmax) return false;
      exonMatch=r->exons[0]->coordMatch(t->exons[0],fuzz);
      /*if (exonMatch) return true;
@@ -469,7 +503,11 @@ void compareLoci2R(GList<GLocus>& loci, GList<GSuperLocus>& cmpdata,
    GLocus* locus=loci[l];
    locus->creset();
    for (int j=0;j<refloci.Count();j++) {
-     if (refloci[j]->start>locus->end) break;
+     //if (refloci[j]->start>locus->end) break;
+     if (refloci[j]->start>locus->end) {
+         if (refloci[j]->start-locus->end > GFF_MAX_LOCUS) break;
+         continue;
+         }
      if (locus->start>refloci[j]->end) continue;
      // then we must have overlap here:
      //if (locus->overlap(refloci[j]->start, refloci[j]->end)) {
@@ -699,17 +737,23 @@ GSeqData* getQryData(int gid, GList<GSeqData>& qdata) {
 const char* findDescr(GffObj* gfobj) {
   if (refdescr.Count()==0) return NULL;
   GStr* s=refdescr.Find(gfobj->getID());
-  if (s==NULL) s=refdescr.Find(gfobj->getGene());
-  if (s!=NULL) 
+  if (s==NULL) {
+       s=refdescr.Find(gfobj->getGeneName());
+       if (s==NULL) s=refdescr.Find(gfobj->getGeneID());
+       }
+  if (s!=NULL)
      return s->chars();
   return NULL;
 }
 
 const char* getGeneID(GffObj* gfobj) {
- const char* s=gfobj->getAttr(ATTR_GENE_NAME);
- if (s!=NULL) return s;
- s=gfobj->getGene();
- return (s==NULL) ? gfobj->getID() : s;
+ //returns anything that might resemble a gene identifier for this transcript
+ //or, if everything fails, returns the transcript ID
+ const char* s=gfobj->getGeneName();
+ if (s) return s;
+ if ((s=gfobj->getGeneID())) return s;
+ if ((s=gfobj->getAttr("Name"))) return s;
+ return gfobj->getID();
 }
 
 const char* getGeneID(GffObj& gfobj) {
@@ -723,12 +767,12 @@ void writeLoci(FILE* f, GList<GLocus> & loci) {
        loc.mrna_maxcov->getGSeqName(),
            loc.mrna_maxcov->strand, loc.start,loc.end);
    //now print all transcripts in this locus, comma delimited
-   int printed=0;
+   int printfd=0;
    for (int i=0;i<loc.mrnas.Count();i++) {
       if (loc.mrnas[i]==loc.mrna_maxcov) continue;
-      if (printed==0) fprintf(f,"%s",loc.mrnas[i]->getID());
+      if (printfd==0) fprintf(f,"%s",loc.mrnas[i]->getID());
           else fprintf(f,",%s",loc.mrnas[i]->getID());
-      printed++;
+      printfd++;
       }
    const char* rdescr=findDescr(loc.mrna_maxcov);
    if (rdescr==NULL)  fprintf(f,"\t\n");
@@ -737,17 +781,17 @@ void writeLoci(FILE* f, GList<GLocus> & loci) {
 }
 
 void printXQ1(FILE* f, int qidx, GList<GLocus>& qloci) {
-  int printed=0;
+  int printfd=0;
   //print
   for (int i=0;i<qloci.Count();i++) {
      if (qloci[i]->qfidx!=qidx) continue;
       for (int j=0;j<qloci[i]->mrnas.Count();j++) {
-        if (printed==0) fprintf(f,"%s",qloci[i]->mrnas[j]->getID());
+        if (printfd==0) fprintf(f,"%s",qloci[i]->mrnas[j]->getID());
             else fprintf(f,",%s",qloci[i]->mrnas[j]->getID());
-        printed++;
+        printfd++;
         }
       }
-  if (printed==0) fprintf(f,"-");
+  if (printfd==0) fprintf(f,"-");
  }
 
 void numXLoci(GList<GXLocus>& xloci, int& last_id) {
@@ -909,37 +953,45 @@ int aa_diff(GXConsensus* c1, GXConsensus* c2) {
 }
 */
 void printConsGTF(FILE* fc, GXConsensus* xc, int xlocnum) {
- //CTData* mdata=((CTData*)m->uptr);
  for (int i=0;i<xc->tcons->exons.Count();i++) {
    fprintf(fc,
-    "%s\t%s\texon\t%d\t%d\t.\t%c\t.\tgene_id \"XLOC_%06d\"; transcript_id \"%s_%08d\"; exon_number \"%d\";",
+     "%s\t%s\texon\t%d\t%d\t.\t%c\t.\tgene_id \"XLOC_%06d\"; transcript_id \"%s_%08d\"; exon_number \"%d\";",
      xc->tcons->getGSeqName(),xc->tcons->getTrackName(),xc->tcons->exons[i]->start, xc->tcons->exons[i]->end, xc->tcons->strand,
        xlocnum, cprefix, xc->id, i+1);
-   //if (i==0) {
-         if (xc->ref!=NULL) 
-     { 
-       const char* s = xc->ref->getAttr(ATTR_GENE_NAME);
-       if (s != NULL)
-       {
-         fprintf (fc, " gene_name \"%s\";",s); 
+    //if (i==0) {
+  const char* gene_name=NULL;
+  if (xc->ref) {
+     gene_name=xc->ref->getGeneName();
+     if (gene_name==NULL) gene_name=xc->ref->getGeneID();
+     if (gene_name) {
+       fprintf (fc, " gene_name \"%s\";", gene_name);
        }
-     }
-         fprintf(fc, " oId \"%s\";",xc->tcons->getID());
-   if (xc->ref)
-   {
-     fprintf(fc, " nearest_ref \"%s\";",xc->ref->getID());
    }
-   fprintf(fc, " class_code \"%c\";",xc->refcode ? xc->refcode: '.');
-         if (xc->tss_id>0)
-           fprintf(fc, " tss_id \"TSS%d\";",xc->tss_id);
-         if (xc->p_id>0)
-           fprintf(fc, " p_id \"P%d\";",xc->p_id);
+   if (!haveRefs) {
+      if (gene_name==NULL && xc->tcons->getGeneName())
+        fprintf (fc, " gene_name \"%s\";", xc->tcons->getGeneName());
+      char* s=xc->tcons->getAttr("nearest_ref", true);
+      if (s) fprintf(fc, " nearest_ref \"%s\";",s);
+      s=xc->tcons->getAttr("class_code", true);
+      if (s) fprintf(fc, " class_code \"%s\";", s);
+      }
+   fprintf(fc, " oId \"%s\";",xc->tcons->getID());
+   if (xc->contained) {
+     fprintf(fc, " contained_in \"%s_%08d\";", cprefix, xc->contained->id);
+     }
+   if (haveRefs) {
+     if (xc->ref!=NULL)
+       fprintf(fc, " nearest_ref \"%s\";",xc->ref->getID());
+     fprintf(fc, " class_code \"%c\";",xc->refcode ? xc->refcode : '.');
+     }
+   if (xc->tss_id>0) fprintf(fc, " tss_id \"TSS%d\";",xc->tss_id);
+   if (xc->p_id>0) fprintf(fc, " p_id \"P%d\";",xc->p_id);
    //      }
    fprintf(fc,"\n");
    }
 }
 
-void tssCluster(GXLocus& xloc, GFaSeqGet *faseq) {
+void tssCluster(GXLocus& xloc) {
   GList<GTssCl> xpcls(true,true,false);
   for (int i=0;i<xloc.tcons.Count();i++) {
     GXConsensus* c=xloc.tcons[i];
@@ -1029,25 +1081,27 @@ void printXLoci(FILE* f, FILE* fc, int qcount, GList<GXLocus>& xloci, GFaSeqGet
   for (int l=0;l<xloci.Count();l++) {
     if (xloci[l]->qloci.Count()==0) continue;
     GXLocus& xloc=*(xloci[l]);
-    tssCluster(xloc, faseq);//cluster and assign tss_id and cds_id to each xconsensus in xloc
+    xloc.checkContainment(); 
+    tssCluster(xloc);//cluster and assign tss_id and cds_id to each xconsensus in xloc
     protCluster(xloc,faseq);
     for (int c=0;c<xloc.tcons.Count();c++) {
-       printConsGTF(fc,xloc.tcons[c],xloc.id);
+       if (showContained || xloc.tcons[c]->contained==NULL)
+         printConsGTF(fc,xloc.tcons[c],xloc.id);
        }
     fprintf(f,"XLOC_%06d\t%s[%c]%d-%d\t", xloc.id,
         xloc.qloci[0]->mrna_maxcov->getGSeqName(),
             xloc.strand, xloc.start,xloc.end);
     //now print all transcripts in this locus, comma delimited
     //first, ref loci, if any
-    int printed=0;
+    int printfd=0;
     if (xloc.rloci.Count()>0) {
        for (int i=0;i<xloc.rloci.Count();i++) {
           for (int j=0;j<xloc.rloci[i]->mrnas.Count();j++) {
-            if (printed==0) fprintf(f,"%s|%s",getGeneID(xloc.rloci[i]->mrnas[j]),
+            if (printfd==0) fprintf(f,"%s|%s",getGeneID(xloc.rloci[i]->mrnas[j]),
                                                     xloc.rloci[i]->mrnas[j]->getID());
                 else fprintf(f,",%s|%s",getGeneID(xloc.rloci[i]->mrnas[j]),
                                                     xloc.rloci[i]->mrnas[j]->getID());
-            printed++;
+            printfd++;
             }
           }
        }
@@ -1122,7 +1176,7 @@ void reportMIntrons(FILE* fm, FILE* fn, FILE* fq, char strand,
 }
 
 
-void processLoci(GSeqData& seqdata, GSeqData* refdata, GFaSeqGet* faseq, int qfidx) {
+void processLoci(GSeqData& seqdata, GSeqData* refdata, int qfidx) {
     //GList<GSeqLoci>& glstloci, GList<GSeqCmpRegs>& cmpdata)
 
   if (refdata!=NULL) {
@@ -1210,7 +1264,7 @@ void printLocus(FILE* f, GLocus& loc, const char* gseqname) {
     }
 }
 
-void collectRNOvl(GSuperLocus& stats, GList<GLocus>& loci, const char* gseqname) {
+void collectRNOvl(GSuperLocus& stats, GList<GLocus>& loci) { //, const char* gseqname) {
   for (int l=0;l<loci.Count();l++) {
     if (loci[l]->cmpovl.Count()==0) {
       stats.m_loci++; //missed ref loci
@@ -1222,7 +1276,7 @@ void collectRNOvl(GSuperLocus& stats, GList<GLocus>& loci, const char* gseqname)
 }
 
 
-void collectCmpData(GSuperLocus& stats, GList<GSuperLocus>& cmpdata, const char* gseqname) {
+void collectCmpData(GSuperLocus& stats, GList<GSuperLocus>& cmpdata) { //, const char* gseqname) {
  for (int c=0;c<cmpdata.Count();c++) {
    stats.addStats(*cmpdata[c]);
    /*
@@ -1263,16 +1317,16 @@ void collectStats(GSuperLocus& stats, GSeqData* seqdata, GSeqData* refdata) {
     }
  */
  //collect data for overlapping superloci (already in seqdata->gstats_f/_r)
- char* gseqname=getGSeqName(seqdata->gseq_id);
- collectCmpData(stats, seqdata->gstats_f, gseqname);
- collectCmpData(stats, seqdata->gstats_r, gseqname);
+ //char* gseqname=getGSeqName(seqdata->gseq_id);
+ collectCmpData(stats, seqdata->gstats_f);
+ collectCmpData(stats, seqdata->gstats_r);
  //for non-overlapping qry loci, always add them as false positives FP
  collectQNOvl(stats, seqdata->loci_f, seqdata->nloci_f);
  collectQNOvl(stats, seqdata->loci_r, seqdata->nloci_r);
  collectQU(stats, seqdata->nloci_u);
  if (!reduceRefs) { //find ref loci with empty cmpovl and add them
-  collectRNOvl(stats, refdata->loci_f, gseqname);
-  collectRNOvl(stats, refdata->loci_r, gseqname);
+  collectRNOvl(stats, refdata->loci_f);
+  collectRNOvl(stats, refdata->loci_r);
   }
 }
 
@@ -1336,13 +1390,6 @@ void reportStats(FILE* fout, const char* setname, GSuperLocus& stotal,
     if (fsp>100.0) fsp=100.0;
     if (fsn>100.0) fsn=100.0;
     fprintf(fout, "Intron chain level: \t%5.1f\t%5.1f\t%5.1f\t%5.1f\n",sn, sp, fsn, fsp);
-    //DEBUG only:
-    /* fprintf(fout, "ichain TP:%d\tref_total: %d\tqry_total: %d\n",
-      ps->ichainTP, ps->total_richains, ps->total_qichains);
-    //DEBUG only:
-    fprintf(fout, "locus  TP:%d\tref_total: %d\tqry_total: %d\n",
-      ps->locusTP, ps->total_rloci, ps->total_qloci);
-      */
     }
   else {
     fprintf(fout, "      Intron level: \t  -  \t  -  \t  -  \t  -  \n");
@@ -1441,7 +1488,7 @@ GffObj* findRefMatch(GffObj& m, GLocus& rloc, int& ovlen) {
       //this must be called only for the head of an equivalency chain
       CTData* rdata=(CTData*)rloc.mrnas[r]->uptr;
       rdata->addOvl('=',&m,olen);
-      if (rdata->eqnext==NULL) rdata->eqnext=&m;
+      //if (rdata->eqnext==NULL) rdata->eqnext=&m;
       }
     }
  if (ret!=NULL)
@@ -1450,9 +1497,11 @@ GffObj* findRefMatch(GffObj& m, GLocus& rloc, int& ovlen) {
  }
 
 
-void addXCons(GXLocus* xloc, GffObj* ref, char ovlcode, GffObj* tcons, GList<GffObj>& ts) {
- GXConsensus* c=new GXConsensus(tcons,ts, ref, ovlcode);
- xloc->tcons.Add(c);
+void addXCons(GXLocus* xloc, GffObj* ref, char ovlcode, GffObj* tcons, CEqList* ts) {
+ GXConsensus* c=new GXConsensus(tcons, ts, ref, ovlcode);
+ //xloc->tcons.Add(c);
+ //this will also check c against the other tcons for containment:
+ xloc->addXCons(c); 
 }
 
 
@@ -1468,8 +1517,9 @@ char getOvlCode(GffObj& m, GffObj& r, int& ovlen) {
      if (jmax==0) { //also single-exon ref
          ovlen=mseg.overlapLen(r.start,r.end);
          int lmax=GMAX(r.covlen, m.covlen);
-         if (ovlen > lmax*0.8) return '='; //generous fuzz matching for single-exon transcripts
-         if (m.covlen<=ovlen+12 && m.covlen<r.covlen) return 'c'; //contained
+         if (ovlen >= lmax*0.8) return '='; //fuzz matching for single-exon transcripts: 80% of the longer one
+         //if (m.covlen<=ovlen+12 && m.covlen<r.covlen) return 'c'; //contained
+         if (m.covlen<r.covlen && ovlen >= m.covlen*0.8) return 'c';
          return 'o'; //just plain overlapping
          }
      //single-exon qry overlaping multi-exon ref
@@ -1603,13 +1653,15 @@ char getRefOvl(GffObj& m, GLocus& rloc, GffObj*& rovl, int& ovlen) {
   rovl=NULL;
   ovlen=0;
   if (m.start>rloc.end || m.end<rloc.start) {
-     //see if it's a polymerase run
+     //see if it's a polymerase run ?
+     /*
        if ((m.strand=='+' && m.end<=rloc.end+polyrun_range) ||
          (m.strand=='-' && m.start>=rloc.start-polyrun_range)) {
             rovl=rloc.mrna_maxcov;
             ((CTData*)m.uptr)->addOvl('p',rloc.mrna_maxcov);
             return 'p';
             }
+     */
      return 0; //unknown -> intergenic space
      }
   for (int i=0;i<rloc.mrnas.Count();i++) {
@@ -1627,46 +1679,127 @@ char getRefOvl(GffObj& m, GLocus& rloc, GffObj*& rovl, int& ovlen) {
   return ((CTData*)m.uptr)->getBestCode();
 }
 
-
+/*
 void findTMatches(GTrackLocus& loctrack, int qcount) {
  //perform an all vs. all ichain-match for all transcripts across all loctrack[i]->qloci
 for (int q=0;q<qcount-1;q++) { //for each qry dataset
   if (loctrack[q]==NULL) continue;
   for (int qi=0;qi<loctrack[q]->Count();qi++) { // for each transcript in q dataset
-    if ((((CTData*)loctrack[q]->Get(qi)->uptr)->eqdata & EQCHAIN_TAG) !=0) continue; //done before
-    for (int n=q+1;n<qcount;n++) { // for each next qry dataset
+    GffObj* qi_t=loctrack[q]->Get(qi);
+    CTData* qi_d=(CTData*)qi_t->uptr;
+    if ((qi_d->eqdata & EQHEAD_TAG) !=0) { //this is set as an EQ chain head already
+         //if (qi_t->exons.Count()>1) 
+          continue; 
+         }
+    for (int n=q+1;n<qcount;n++) { // for every successor dataset
        if (loctrack[n]==NULL) continue;
        for (int ni=0;ni<loctrack[n]->Count();ni++) {
-          if (((CTData*)loctrack[n]->Get(ni)->uptr)->eqdata !=0) continue;
+          GffObj* ni_t=loctrack[n]->Get(ni);
+          CTData* ni_d=(CTData*)ni_t->uptr;
+          //if (ni_d->eqdata!=0) continue; //already part of an EQ chain
+          //single exon transfrags have special treatment:
+          bool s_match=(ni_t->exons.Count()==1 && qi_t->exons.Count()==1);
+          if (ni_d->eqdata!=0 && !s_match) continue; //already part of an EQ chain
+          if (ni_d->eqnext!=NULL) continue;
           int ovlen=0;
-          if (tMatch(*(loctrack[q]->Get(qi)),*(loctrack[n]->Get(ni)), ovlen, true)) {
-             ((CTData*)loctrack[q]->Get(qi)->uptr)->eqnext=loctrack[n]->Get(ni);
-             if (((CTData*)loctrack[q]->Get(qi)->uptr)->eqdata ==0) {//only start of chain is tagged
+          
+          if (qi_d->eqnext!=NULL) {
+              if (!s_match) continue;
+              //test all in the EQ list for a match
+              bool matchFound=false;
+              CTData* next_eq_d=qi_d;
+              if (tMatch(*qi_t, *ni_t, ovlen, true)) {
+                 matchFound=true;
+                 }
+               else {
+                 while (next_eq_d->eqnext!=NULL) {
+                     if (tMatch(*(next_eq_d->eqnext), *ni_t, ovlen, true)) {
+                         matchFound=true;
+                         break;
+                         }
+                     next_eq_d=(CTData*)(next_eq_d->eqnext->uptr);
+                     } //last in the chain
+                 }
+              if (matchFound) {
+                 //add this to the end of the EQ chain instead
+                 next_eq_d=(CTData*)(qi_d->eqnext->uptr);
+                 while (next_eq_d->eqnext!=NULL) {
+                        next_eq_d=(CTData*)(next_eq_d->eqnext->uptr);
+                        } //last in the chain
+                 next_eq_d->eqnext=ni_t;
+                 ni_d->eqdata=n+1;
+                 ni_d->eqnext=NULL; ///TEST
+                 }
+              }
+           else {
+              if (tMatch(*qi_t,*ni_t, ovlen, true)) {
+                 qi_d->eqnext=ni_t;
+                 ni_d->eqnext=NULL; ///TEST
+                 if (qi_d->eqdata == 0) {//only start of chain is tagged
+                   qi_d->eqdata = ((q+1) | EQHEAD_TAG);
+                   }
+                 ni_d->eqdata=n+1; //EQ chain member only marked with qry# (1-based)
+                 }
+              } //multi-exon case
+          } //for each transfrag in the next qry dataset
+       
+       if (qi_d->eqnext!=NULL && qi_t->exons.Count()>1) break;
+         //part of a chain already, skip other datasets
+       } // for each successor dataset
+    } //for each transcript in qry dataset
+  } //for each qry dataset
+}
+*/
 
-               ((CTData*)loctrack[q]->Get(qi)->uptr)->eqdata= ((q+1) | EQCHAIN_TAG);
-               }
-             ((CTData*)loctrack[n]->Get(ni)->uptr)->eqdata=n+1;
+void findTMatches(GTrackLocus& loctrack, int qcount) {
+ //perform an all vs. all ichain-match for all transcripts across all loctrack[i]->qloci
+for (int q=0;q<qcount-1;q++) { //for each qry dataset
+  if (loctrack[q]==NULL) continue;
+  for (int qi=0;qi<loctrack[q]->Count();qi++) { // for each transcript in q dataset
+    GffObj* qi_t=loctrack[q]->Get(qi);
+    CTData* qi_d=(CTData*)qi_t->uptr;
+    if (qi_d->eqlist!=NULL && qi_t->exons.Count()>1) {
+         continue; //this is part of an EQ chain already
+         }
+    for (int n=q+1;n<qcount;n++) { // for every successor dataset
+       if (loctrack[n]==NULL) continue;
+       for (int ni=0;ni<loctrack[n]->Count();ni++) {
+          GffObj* ni_t=loctrack[n]->Get(ni);
+          CTData* ni_d=(CTData*)ni_t->uptr;
+          bool singleExon=(ni_t->exons.Count()==1 && qi_t->exons.Count()==1);
+          if (ni_d->eqlist!=NULL && 
+                 (ni_d->eqlist==qi_d->eqlist || !singleExon)) continue;
+          int ovlen=0;
+          if ((ni_d->eqlist==qi_d->eqlist && qi_d->eqlist!=NULL) ||
+                  tMatch(*qi_t,*ni_t, ovlen, singleExon)) {
+             qi_d->joinEqList(ni_t);
              }
           }
-       if (((CTData*)loctrack[q]->Get(qi)->uptr)->eqnext!=NULL) break;
-                //start of chain already set
-       } // for each next qry dataset
-    } //for each transcript
-  } //for qry dataset
+       } // for each successor dataset
+    } //for each transcript in qry dataset
+  } //for each qry dataset
 }
 
+
+int cmpTData_qset(const pointer* p1, const pointer* p2) {
+ CTData* d1=(CTData*)(((GffObj*)p1)->uptr);
+ CTData* d2=(CTData*)(((GffObj*)p2)->uptr);
+ return (d1->qset - d2->qset);
+ }
+
 void printITrack(FILE* ft, GList<GffObj>& mrnas, int qcount, int& cnum) {
   for (int i=0;i<mrnas.Count();i++) {
    GffObj& qt=*(mrnas[i]);
    CTData* qtdata=(CTData*)qt.uptr;
    int qfidx=qtdata->qset;
    char ovlcode=qtdata->classcode;
-   GList<GffObj> eqchain(false,false,false);
+   //GList<GffObj> eqchain(false,false,false);
+   CEqList* eqchain=qtdata->eqlist;
    GffObj* ref=NULL; //related ref -- it doesn't have to be fully matching
    GffObj* eqref=NULL; //fully ichain-matching ref
    GffObj* tcons=NULL; //"consensus" (largest) transcript for a clique
    int tmaxcov=0;
-   eqchain.Add(&qt);
+   //eqchain.Add(&qt);
    eqref=qtdata->eqref;
    if (qtdata->ovls.Count()>0 && qtdata->ovls[0]->mrna!=NULL) {
        //if it has ovlcode with a ref
@@ -1674,21 +1807,25 @@ void printITrack(FILE* ft, GList<GffObj>& mrnas, int qcount, int& cnum) {
        //consistency check: qtdata->ovls[0]->code==ovlcode
        tcons=eqref;
        if (tcons!=NULL) tmaxcov=tcons->covlen;
-       /*
-       if (ovlcode=='=') {
-          tcons=ref;
-          tmaxcov=tcons->covlen;
-          }
-       */
        }
    //chain pre-check
    if (tcons==NULL || mrnas[i]->covlen>tmaxcov) {
        tcons=mrnas[i];
        tmaxcov=tcons->covlen;
        }
-   if ((qtdata->eqdata & EQCHAIN_TAG)!=0) {//head of a equivalency chain
-      //if (ovlcode!='=' && qtdata->eqnext!=NULL && () {
+   if (qtdata->isEqHead()) {//head of a equivalency chain
       //check if all transcripts in this chain have the same ovlcode
+      for (int k=0;k<qtdata->eqlist->Count();k++) {
+         GffObj* m=qtdata->eqlist->Get(k);
+        if (m->covlen>tmaxcov) {
+            tmaxcov=m->covlen;
+            tcons=m;
+            }
+        if (ovlcode!='=' && ovlcode!='.' && ((CTData*)m->uptr)->getBestCode()!=ovlcode) {
+              ovlcode='.'; //non-uniform ovlcode
+              }
+         }
+      /*
       GffObj* m=mrnas[i];
       while (((CTData*)m->uptr)->eqnext!=NULL) {
         m=((CTData*)m->uptr)->eqnext;
@@ -1702,12 +1839,16 @@ void printITrack(FILE* ft, GList<GffObj>& mrnas, int qcount, int& cnum) {
               //break;
               }
         } //while elements in chain
+       */
+       
       }//chain check
    //if (ovlcode=='p') ref=NULL; //ignore polymerase runs?
    if (ovlcode==0 || ovlcode=='-') ovlcode = (ref==NULL) ? 'u' : '.';
    //-- print columns 1 and 2 as LOCUS_ID and TCONS_ID
-   bool chainHead=(qtdata->eqnext!=NULL && ((qtdata->eqdata & EQCHAIN_TAG)!=0));
-   bool noChain=((qtdata->eqdata & EQCHAIN_TAGMASK)==0);
+   //bool chainHead=(qtdata->eqnext!=NULL && ((qtdata->eqdata & EQHEAD_TAG)!=0));
+   bool chainHead=qtdata->isEqHead();
+   //bool noChain=((qtdata->eqdata & EQCHAIN_TAGMASK)==0);
+   bool noChain=(eqchain==NULL);
    if (chainHead || noChain) {
      cnum++;
      if (ft!=NULL) fprintf(ft,"%s_%08d\t",cprefix,cnum);
@@ -1722,58 +1863,72 @@ void printITrack(FILE* ft, GList<GffObj>& mrnas, int qcount, int& cnum) {
               }
          }
       else {
-        //if (ft!=NULL) fprintf(ft,"noXLocus\t"); //should NEVER happen!
-        int fidx=(qtdata->eqdata & 0x0FFF);
+        //should NEVER happen!
+        int fidx=qtdata->qset;
         GError("Error: no XLocus created for transcript %s (file %s) [%d, %d], on %s%c:%d-%d\n", qt.getID(),
                            qryfiles[qtdata->locus->qfidx]->chars(), qtdata->locus->qfidx, fidx, qt.getGSeqName(), qt.strand, qt.start, qt.end);
         }
-     addXCons(xloc, ref, ovlcode, &qt, eqchain); //store for now; print later
+     addXCons(xloc, ref, ovlcode, tcons, eqchain);
      } // if chain head or uniq entry (not part of a chain)
-   if (ft==NULL) { eqchain.Clear(); continue; }
+   if (ft==NULL) continue;
    if (chainHead) {
-     //this is the start of a equivalence class as a printing chain
-     if (ref!=NULL) fprintf(ft,"%s|%s\t%c", getGeneID(ref),ref->getID(), ovlcode);
-               else {
-                 //if (ovlcode=='.') fprintf(ft,"-        \t-");
-                 //   else 
-                       fprintf(ft,"-        \t%c", ovlcode);
-                 }
-     GffObj* m=mrnas[i];
-     CTData* mdata=(CTData*)m->uptr;
-
-     int lastpq=-1;
-     //for (int ptab=(((CTData*)m->uptr)->eqdata & 0x07FF)-lastpq-1;ptab>=0;ptab--)
-     for (int ptab=mdata->qset-lastpq; ptab>0;ptab--)
-            if (ptab>1) fprintf(ft,"\t-");
-                   else fprintf(ft,"\t");
-     //lastpq=((CTData*)m->uptr)->eqdata & 0x07FF;
-     lastpq=mdata->qset;
-     fprintf(ft,"q%d:%s|%s|%d|%8.6f|%8.6f|%8.6f|%8.6f|%d", lastpq+1, getGeneID(m), m->getID(),
-         iround(m->gscore/10), mdata->FPKM, mdata->conf_lo, mdata->conf_hi, mdata->cov, m->covlen);
-     //traverse linked list of matching transcripts
-     while (mdata->eqnext!=NULL) {
-        m=mdata->eqnext;
-        mdata=(CTData*)m->uptr;
-        for (int ptab=mdata->qset-lastpq;ptab>0;ptab--)
-            if (ptab>1) fprintf(ft,"\t-");
-                   else fprintf(ft,"\t");
-        lastpq = mdata->qset;
-        fprintf(ft,"q%d:%s|%s|%d|%8.6f|%8.6f|%8.6f|%8.6f|%d", lastpq+1, getGeneID(m), m->getID(),
-           iround(m->gscore/10), mdata->FPKM,mdata->conf_lo,mdata->conf_hi,mdata->cov, m->covlen);
-        } //traverse and print row
-     for (int ptab=qcount-lastpq-1;ptab>0;ptab--)
-           fprintf(ft,"\t-");
-     fprintf(ft,"\n");
-     eqchain.Clear();
-     continue;
-     } //start of eq class (printing chain)
-
-   if (!noChain) { eqchain.Clear(); continue; }//part of a matching chain, dealt with previously
+      //this is the start of a equivalence class as a printing chain
+      if (ref!=NULL) fprintf(ft,"%s|%s\t%c", getGeneID(ref),ref->getID(), ovlcode);
+                else fprintf(ft,"-\t%c", ovlcode);
+      GffObj* m=mrnas[i];
+      CTData* mdata=(CTData*)m->uptr;
+      
+      int lastpq=-1;
+      /*
+      for (int ptab=mdata->qset-lastpq; ptab>0;ptab--)
+             if (ptab>1) fprintf(ft,"\t-");
+                    else fprintf(ft,"\t");
+      lastpq=mdata->qset;
+      fprintf(ft,"q%d:%s|%s|%d|%8.6f|%8.6f|%8.6f|%8.6f|%d", lastpq+1, getGeneID(m), m->getID(),
+          iround(m->gscore/10), mdata->FPKM, mdata->conf_lo, mdata->conf_hi, mdata->cov, m->covlen);
+      //traverse linked list of matching transcripts
+      while (mdata->eqnext!=NULL) {
+         m=mdata->eqnext;
+         mdata=(CTData*)m->uptr;
+         for (int ptab=mdata->qset-lastpq;ptab>0;ptab--)
+             if (ptab>1) fprintf(ft,"\t-");
+                    else fprintf(ft,"\t");
+         lastpq = mdata->qset;
+         fprintf(ft,"q%d:%s|%s|%d|%8.6f|%8.6f|%8.6f|%8.6f|%d", lastpq+1, getGeneID(m), m->getID(),
+            iround(m->gscore/10), mdata->FPKM,mdata->conf_lo,mdata->conf_hi,mdata->cov, m->covlen);
+         } //traverse and print row
+      */
+      eqchain->setUnique(false);
+      eqchain->setSorted((GCompareProc*) cmpTData_qset);
+      
+      for (int k=0;k<eqchain->Count();k++) {
+         m=eqchain->Get(k);
+         mdata=(CTData*)m->uptr;
+         if (mdata->qset==lastpq) {
+            //shouldn't happen, unless this input set is messed up (has duplicates/redundant transfrags)
+            fprintf(ft,",%s|%s|%d|%8.6f|%8.6f|%8.6f|%8.6f|%d", getGeneID(m), m->getID(),
+               iround(m->gscore/10), mdata->FPKM,mdata->conf_lo,mdata->conf_hi,mdata->cov, m->covlen);
+            continue;
+            }
+         for (int ptab=mdata->qset-lastpq;ptab>0;ptab--)
+             if (ptab>1) fprintf(ft,"\t-");
+                    else fprintf(ft,"\t");
+         lastpq = mdata->qset;
+         fprintf(ft,"q%d:%s|%s|%d|%8.6f|%8.6f|%8.6f|%8.6f|%d", lastpq+1, getGeneID(m), m->getID(),
+            iround(m->gscore/10), mdata->FPKM,mdata->conf_lo,mdata->conf_hi,mdata->cov, m->covlen);
+         }
+      for (int ptab=qcount-lastpq-1;ptab>0;ptab--)
+            fprintf(ft,"\t-");
+      fprintf(ft,"\n");
+      continue;
+      } //start of eq class (printing chain)
+
+   if (eqchain!=NULL) continue; //part of a matching chain, dealt with previously
 
    //--------- not in an ichain-matching class, print as singleton
 
    if (ref!=NULL) fprintf(ft,"%s|%s\t%c",getGeneID(ref), ref->getID(), ovlcode);
-             else fprintf(ft,"-        \t%c",ovlcode);
+             else fprintf(ft,"-\t%c",ovlcode);
    for (int ptab=qfidx;ptab>=0;ptab--)
       if (ptab>0) fprintf(ft,"\t-");
              else fprintf(ft,"\t");
@@ -1797,24 +1952,34 @@ void findTRMatch(GTrackLocus& loctrack, int qcount, GLocus& rloc) {
     CTData* qtdata=(CTData*)qt.uptr;
     GffObj* rmatch=NULL; //== ref match for this row
     int rovlen=0;
-    if (qtdata->eqnext!=NULL && ((qtdata->eqdata & EQCHAIN_TAG)!=0)) {
-           // && qtdata->eqref==NULL) {   //qry equivalency list starts here
+    //if (qtdata->eqnext!=NULL && ((qtdata->eqdata & EQHEAD_TAG)!=0)) { 
+    if (qtdata->isEqHead()) {
+        //EQ chain head -- transfrag equivalency list starts here
         if (qtdata->eqref==NULL) { //find rloc overlap
            if (qt.overlap(rloc.start, rloc.end)) {
-              rmatch=findRefMatch(qt, rloc, rovlen);
-              }
+                 rmatch=findRefMatch(qt, rloc, rovlen);
+                 }
            } else rmatch=qtdata->eqref;
-        GffObj* m=loctrack[q]->Get(qi);
-        //int lastpq=((CTData*)m->uptr)->eqdata & 0x07FF;
-        //traverse linked list of matching transcripts
-        while (((CTData*)m->uptr)->eqnext!=NULL) {
-           m=((CTData*)m->uptr)->eqnext;
-           if (rmatch!=NULL) {
-             ((CTData*)m->uptr)->addOvl('=',rmatch,rovlen);
-             }
-           //lastpq = ((CTData*)m->uptr)->eqdata & 0x07FF;
-           } //traverse qry data sets
-        if (rmatch!=NULL) continue;
+       if (rmatch!=NULL) {
+         /*
+          GffObj* m=loctrack[q]->Get(qi);
+          //traverse linked list of matching transcripts
+          while (((CTData*)m->uptr)->eqnext!=NULL) {
+            m=((CTData*)m->uptr)->eqnext;
+            if (rmatch!=NULL) {
+              ((CTData*)m->uptr)->addOvl('=',rmatch,rovlen);
+              }
+            } //traverse qry data sets
+          continue;
+          }
+         */
+          for (int k=0;k<qtdata->eqlist->Count();k++) {
+            GffObj* m=qtdata->eqlist->Get(k);
+            ((CTData*)m->uptr)->addOvl('=',rmatch,rovlen);
+            continue;
+            }
+         }
+        //if (rmatch!=NULL) continue;
         } //equivalence class (chain of intron-matching)
     //if ((qtdata->eqdata & EQCHAIN_TAGMASK)!=0) continue; //part of a matching chain, dealt with previously
 
@@ -1838,56 +2003,34 @@ void findTRMatch(GTrackLocus& loctrack, int qcount, GLocus& rloc) {
 
 
 bool inPolyRun(char strand, GffObj& m, GList<GLocus>* rloci, int& rlocidx) {
- if (rloci==NULL || rloci->Count()==0) return false;
- if (rlocidx<0 || abs((int)(rloci->Get(rlocidx)->start-m.start))>40000){
-     GLocus floc;
-     floc.start=m.start;
-     floc.end=m.end;
-     rloci->Found(&floc,rlocidx);
-     if (rlocidx>=rloci->Count() || rlocidx<0) rlocidx=rloci->Count()-1;
-     }
- GLocus* rloc=rloci->Get(rlocidx);
- //make sure rloc is the closest upstream locus
- if (strand=='+') {
-    while ((int)(m.start-rloc->end)<-4) { //reposition rlocidx if needed
-          if (rlocidx==0) return false;
-          rlocidx--;
+ //we are only here if there is no actual overlap between m and any locus in rloci
+ if (rloci==NULL || rloci->Count()==0) return false; // || m.exons.Count()>1
+  if (strand=='-') {
+        rlocidx=qsearch_loci(m.end, *rloci);
+        //returns index of locus starting just ABOVE m.end
+        // or -1 if last locus start <= m.end
+        GLocus* rloc=NULL;
+        if (rlocidx<0) return false;
+        while (rlocidx<rloci->Count()) {
+           rloc=rloci->Get(rlocidx);
+           if (rloc->start>m.end+polyrun_range) break;
+           if (rloc->start+6>m.end) return true;
+           rlocidx++;
+           }
+        }
+      else { // strand == '+' (or '.' ?)
+        rlocidx=qsearch_loci(m.end, *rloci);
+        GLocus* rloc=NULL;
+        //returns index of closest locus starting ABOVE m.end
+        // or -1 if last locus start <= m.end
+        if (rlocidx<0) rlocidx=rloci->Count(); //this may actually start below m.end
+        while ((--rlocidx)>=0) {
           rloc=rloci->Get(rlocidx);
+          if (m.start>rloc->start+GFF_MAX_LOCUS) break;
+          if (m.start+6>rloc->end && m.start<rloc->end+polyrun_range) return true;
           }
-    int rd=(int)(m.start-rloc->end);
-    int ridx=rlocidx;
-    while (ridx<rloci->Count()-1) {
-        ridx++;
-        int newrd=(int)(m.start-rloci->Get(ridx)->end);
-        if (newrd>0 && newrd<rd) {
-            rlocidx=ridx;
-            rloc=rloci->Get(rlocidx);
-            rd=newrd;
-            }
-         else break;
         }
-    return (m.end>rloc->end && m.start<rloc->end+polyrun_range);
-    } //forward strand
- else { //reverse strand
-   while ((int)(rloc->start-m.end)<-4) { //reposition rlocidx if needed
-         if (rlocidx==rloci->Count()-1) return false;
-         rlocidx++;
-         rloc=rloci->Get(rlocidx);
-         }
-   int rd=(int)(rloc->start-m.end);
-   int ridx=rlocidx;
-   while (ridx>0) {
-       ridx--;
-       int newrd=rloci->Get(ridx)->start-m.end;
-       if (newrd>0 && newrd<rd) {
-           rlocidx=ridx;
-           rloc=rloci->Get(rlocidx);
-           rd=newrd;
-           }
-        else break;
-       }
-    return (m.start<rloc->start && m.end+polyrun_range>rloc->start);
-    }
+  return false;
 }
 
 CTData* getBestOvl(GffObj& m) {
@@ -1899,12 +2042,17 @@ CTData* getBestOvl(GffObj& m) {
 }
 
 void reclass_XStrand(GList<GffObj>& mrnas, GList<GLocus>* rloci) {
+  //checking for relationship with ref transcripts on opposite strand
   if (rloci==NULL or rloci->Count()<1) return;
   int j=0;//current rloci index
   for (int i=0;i<mrnas.Count();i++) {
      GffObj& m=*mrnas[i];
      char ovlcode=((CTData*)m.uptr)->getBestCode();
-     if (ovlcode=='u' || ovlcode<47) { //look for overlaps between mrnas and rloci
+     if (ovlcode>47 && strchr("=cjeo",ovlcode)!=NULL) continue;
+     //if (ovlcode<47 || strchr("uirp",ovlcode)!=NULL)
+     //if (ovlcode<47 || strchr("=cjeo",ovlcode)==NULL)
+        {
+         //incCodeCount(ovlcode);
          GLocus* rloc=rloci->Get(j);
         CHECK_RLOC:
          if (rloc->start>m.end) continue; //check next transfrag
@@ -1922,7 +2070,7 @@ void reclass_XStrand(GList<GffObj>& mrnas, GList<GLocus>* rloci) {
          //  then if m is contained within an intron -> 'i'
          //  otherwise it's just a plain cross-strand overlap: 'x'
          int jm=0;
-         do { //while rloci overlap to this transfrag (m)
+         do { //while rloci overlap this transfrag (m)
            bool is_shadow=false;
            GffObj* sovl=NULL;
            bool is_intraintron=false;
@@ -1954,7 +2102,7 @@ void reclass_XStrand(GList<GffObj>& mrnas, GList<GLocus>* rloci) {
            bool xcode=true;
            if (is_shadow) { ((CTData*)m.uptr)->addOvl('s', sovl); xcode=false; }
                  // else
-           if (is_intraintron) { ((CTData*)m.uptr)->addOvl('i', iovl); xcode=false; }
+           if (ovlcode!='i' && is_intraintron) { ((CTData*)m.uptr)->addOvl('i', iovl); xcode=false; }
            if (xcode) {
                    // just plain overlap, find the overlapping mrna in rloc
                    GffObj* maxovl=NULL;
@@ -1970,7 +2118,7 @@ void reclass_XStrand(GList<GffObj>& mrnas, GList<GLocus>* rloci) {
                    } //'x'
            jm++;
            } while (j+jm<rloci->Count() && rloci->Get(j+jm)->overlap(m));
-         } // 'u' code testing
+        } // code testing across strands
      } //for each transfrag
 }
 
@@ -1982,7 +2130,7 @@ void reclass_mRNAs(char strand, GList<GffObj>& mrnas, GList<GLocus>* rloci, GFaS
     //if (ovlcode=='u' || ovlcode=='i' || ovlcode==0) {
     if (ovlcode=='u' || ovlcode<47) {
       //check for overlaps with ref transcripts on the other strand
-      if (inPolyRun(strand, m, rloci, rlocidx)) {
+      if (m.exons.Count()==1 && inPolyRun(strand, m, rloci, rlocidx)) {
          ((CTData*)m.uptr)->addOvl('p',rloci->Get(rlocidx)->mrna_maxcov);
          }
       else { //check for repeat content
@@ -2012,42 +2160,47 @@ void reclassLoci(char strand, GList<GLocus>& qloci, GList<GLocus>* rloci, GFaSeq
 //for a single genomic sequence, all qry data and ref data is stored in gtrack
 //check for all 'u' transfrags if they are repeat ('r') or polymerase run 'p' or anything else
 void umrnaReclass(int qcount,  GSeqTrack& gtrack, FILE** ftr, GFaSeqGet* faseq=NULL) {
-  for (int q=0;q<qcount;q++) {
-    if (gtrack.qdata[q]==NULL) continue; //no transcripts in this q dataset for this genomic seq
-    reclassLoci('+', gtrack.qdata[q]->loci_f, gtrack.rloci_f, faseq);
-    reclassLoci('-', gtrack.qdata[q]->loci_r, gtrack.rloci_r, faseq);
-    reclass_mRNAs('+', gtrack.qdata[q]->umrnas, gtrack.rloci_f, faseq);
-    reclass_mRNAs('-', gtrack.qdata[q]->umrnas, gtrack.rloci_r, faseq);
-    //and also check for special cases with cross-strand overlaps:
-    reclass_XStrand(gtrack.qdata[q]->mrnas_f, gtrack.rloci_r);
-    reclass_XStrand(gtrack.qdata[q]->mrnas_r, gtrack.rloci_f);
-    // print all tmap data here here:
-    for (int i=0;i<gtrack.qdata[q]->tdata.Count();i++) {
-      CTData* mdata=gtrack.qdata[q]->tdata[i];
-      if (mdata->mrna==NULL) continue; //invalidated -- removed earlier
-      //GLocus* rlocus=NULL;
-      mdata->classcode='u';
-      GffObj* ref=NULL;
-      if (mdata->ovls.Count()>0) {
-            mdata->classcode=mdata->ovls[0]->code;
-            ref=mdata->ovls[0]->mrna;
+    for (int q=0;q<qcount;q++) {
+        if (gtrack.qdata[q]==NULL) continue; //no transcripts in this q dataset for this genomic seq
+        reclassLoci('+', gtrack.qdata[q]->loci_f, gtrack.rloci_f, faseq);
+        reclassLoci('-', gtrack.qdata[q]->loci_r, gtrack.rloci_r, faseq);
+        reclass_mRNAs('+', gtrack.qdata[q]->umrnas, gtrack.rloci_f, faseq);
+        reclass_mRNAs('-', gtrack.qdata[q]->umrnas, gtrack.rloci_r, faseq);
+        //and also check for special cases with cross-strand overlaps:
+        reclass_XStrand(gtrack.qdata[q]->mrnas_f, gtrack.rloci_r);
+        reclass_XStrand(gtrack.qdata[q]->mrnas_r, gtrack.rloci_f);
+        // print all tmap data here here:
+        for (int i=0;i<gtrack.qdata[q]->tdata.Count();i++) {
+            CTData* mdata=gtrack.qdata[q]->tdata[i];
+            if (mdata->mrna==NULL) continue; //invalidated -- removed earlier
+            //GLocus* rlocus=NULL;
+            mdata->classcode='u';
+            GffObj* ref=NULL;
+            if (mdata->ovls.Count()>0) {
+                mdata->classcode=mdata->ovls[0]->code;
+                ref=mdata->ovls[0]->mrna;
             }
-      //if (mdata->classcode<33) mdata->classcode='u';      
-      if (mdata->classcode<47) mdata->classcode='u'; // if 0, '-' or '.'
-      if (tmapFiles) {
-         if (ref!=NULL) {
-              fprintf(ftr[q],"%s\t%s\t",getGeneID(ref),ref->getID());
-              //rlocus=((CTData*)(ref->uptr))->locus;
-              }
-           else fprintf(ftr[q],"-          \t-          \t");
-      //fprintf(ftr[q],"%c\t%s\t%d\t%8.6f\t%8.6f\t%d\n", ovlcode, mdata->mrna->getID(),
-      //    iround(mdata->mrna->gscore/10), mdata->FPKM, mdata->cov, mdata->mrna->covlen);
-         const char* mlocname = (mdata->locus!=NULL) ? mdata->locus->mrna_maxcov->getID() : mdata->mrna->getID();
-         fprintf(ftr[q],"%c\t%s\t%s\t%d\t%8.6f\t%8.6f\t%8.6f\t%8.6f\t%d\t%s\n", mdata->classcode, getGeneID(mdata->mrna), mdata->mrna->getID(),
-           iround(mdata->mrna->gscore/10), mdata->FPKM, mdata->conf_lo,mdata->conf_hi, mdata->cov, mdata->mrna->covlen, mlocname);
-         }
-    } //for each tdata
-  } //for each qdata
+            //if (mdata->classcode<33) mdata->classcode='u';      
+            if (mdata->classcode<47) mdata->classcode='u'; // if 0, '-' or '.'
+            if (tmapFiles) {
+                char ref_match_len[2048];
+                if (ref!=NULL) {
+                    sprintf(ref_match_len, "%d",ref->covlen);
+                    fprintf(ftr[q],"%s\t%s\t",getGeneID(ref),ref->getID());
+                    //rlocus=((CTData*)(ref->uptr))->locus;
+                }
+                else {
+                    fprintf(ftr[q],"-\t-\t");
+                    strcpy(ref_match_len, "-");
+                }
+                //fprintf(ftr[q],"%c\t%s\t%d\t%8.6f\t%8.6f\t%d\n", ovlcode, mdata->mrna->getID(),
+                //    iround(mdata->mrna->gscore/10), mdata->FPKM, mdata->cov, mdata->mrna->covlen);
+                const char* mlocname = (mdata->locus!=NULL) ? mdata->locus->mrna_maxcov->getID() : mdata->mrna->getID();
+                fprintf(ftr[q],"%c\t%s\t%s\t%d\t%8.6f\t%8.6f\t%8.6f\t%8.6f\t%d\t%s\t%s\n", mdata->classcode, getGeneID(mdata->mrna), mdata->mrna->getID(),
+                        iround(mdata->mrna->gscore/10), mdata->FPKM, mdata->conf_lo,mdata->conf_hi, mdata->cov, mdata->mrna->covlen, mlocname, ref_match_len);
+            }
+        } //for each tdata
+    } //for each qdata
 }
 
 void buildXLoci(GTrackLocus& loctrack, int qcount, GSeqTrack& gtrack, char strand,
@@ -2069,7 +2222,6 @@ void buildXLoci(GTrackLocus& loctrack, int qcount, GSeqTrack& gtrack, char stran
      xloci=dest_xloci;
      dest_xloci=NULL;
    }
-
  for (int q=-1;q<qcount;q++) {
    GList<GLocus>* wrkloci=NULL;
    if (q<0) {
@@ -2082,6 +2234,7 @@ void buildXLoci(GTrackLocus& loctrack, int qcount, GSeqTrack& gtrack, char stran
       if (loctrack[q]==NULL) continue;
       wrkloci = &(loctrack[q]->qloci);
       }
+   
    for (int t=0;t<wrkloci->Count();t++) {
       GLocus* loc=wrkloci->Get(t);
       int xfound=0; //count of parent xloci
@@ -2089,7 +2242,10 @@ void buildXLoci(GTrackLocus& loctrack, int qcount, GSeqTrack& gtrack, char stran
       GArray<int> mrgxloci(true);
       for (int xl=0;xl<xloci->Count();xl++) {
          GXLocus& xloc=*(xloci->Get(xl));
-         if (xloc.start>loc->end) break;
+         if (xloc.start>loc->end) {
+            if (xloc.start-loc->end > GFF_MAX_LOCUS) break;
+            continue;
+            }
          if (loc->start>xloc.end) continue;
          if (xloc.add_Locus(loc)) {
             xfound++;
@@ -2098,6 +2254,7 @@ void buildXLoci(GTrackLocus& loctrack, int qcount, GSeqTrack& gtrack, char stran
          } //for each existing Xlocus
       //if (xfound<0) continue;
       if (xfound==0) {
+         //GXLocus *newxloc=new GXLocus(loc);
          xloci->Add(new GXLocus(loc));
          }
       else if (xfound>1) {
@@ -2174,7 +2331,7 @@ void recheckUmrnas(GSeqData* gseqdata, GList<GffObj>& mrnas,
    }
 }
 
-void umrnasXStrand(GList<GXLocus>& xloci, int qcount, GSeqTrack& gtrack) {
+void umrnasXStrand(GList<GXLocus>& xloci, GSeqTrack& gtrack) {
   //try to determine the strand of unoriented transfrags based on possible overlaps
   //with other, oriented transfrags
  for (int x=0;x<xloci.Count();x++) {
@@ -2212,7 +2369,7 @@ void umrnasXStrand(GList<GXLocus>& xloci, int qcount, GSeqTrack& gtrack) {
 }
 
 //cluster loci across all datasets
-void xclusterLoci(int qcount, int gsid, char strand, GSeqTrack& gtrack, FILE* fl) {
+void xclusterLoci(int qcount, char strand, GSeqTrack& gtrack) {
   //gtrack holds data for all input qry datasets for a chromosome/contig
   //cluster QLoci
   GList<GTrackLocus> loctracks(true,true,false);
@@ -2221,29 +2378,32 @@ void xclusterLoci(int qcount, int gsid, char strand, GSeqTrack& gtrack, FILE* fl
   GList<GLocus>* wrkloci=NULL;
   //build xloci without references first
   //then add references only if they overlap an existing xloci
+  
   int nq=0;
   for (int q=0;q<=qcount+1;q++) {
     bool refcheck=false;
-    if (q==qcount) { // check the unoriented loci
+    if (q==qcount) { // check the unoriented loci for each query file
        while (nq<qcount &&
               (gtrack.qdata[nq]==NULL || gtrack.qdata[nq]->nloci_u.Count()==0))
-                 nq++;
+                 nq++; //skip query files with no unoriented loci
        if (nq<qcount) {
              wrkloci=&(gtrack.qdata[nq]->nloci_u);
              nq++;
              if (nq<qcount) q--; //so we can fetch the next nq in the next q cycle
              }
+          else continue; //no more q files with unoriented loci
        }
     else if (q==qcount+1) { // check the reference loci
            if (strand=='+') wrkloci=gtrack.rloci_f;
                        else wrkloci=gtrack.rloci_r;
+            
            if (wrkloci==NULL) break; //no ref loci here
            refcheck=true;
            }
      else  {
           if (gtrack.qdata[q]==NULL) continue;
           if (strand=='+') wrkloci=&(gtrack.qdata[q]->loci_f);
-             else if (strand=='-') wrkloci=&(gtrack.qdata[q]->loci_r);
+                      else wrkloci=&(gtrack.qdata[q]->loci_r);
          }
    // now do the all-vs-all clustering thing:
    for (int t=0;t<wrkloci->Count();t++) {
@@ -2260,7 +2420,6 @@ void xclusterLoci(int qcount, int gsid, char strand, GSeqTrack& gtrack, FILE* fl
             mrgloctracks.Add(xl);
             }
          } //for each existing Xlocus
-      //if (xfound<0) continue;
       if (xfound==0) {
          if (!refcheck) //we really don't care about ref-only clusters
            loctracks.Add(new GTrackLocus(loc));
@@ -2278,7 +2437,6 @@ void xclusterLoci(int qcount, int gsid, char strand, GSeqTrack& gtrack, FILE* fl
  for (int i=0;i<loctracks.Count();i++) {
    if (!loctracks[i]->hasQloci) continue; //we really don't care here about reference-only clusters
    GTrackLocus& loctrack=*loctracks[i];
-   //fprintf(fl,"-      \t%s:%d-%d(%c)", getGSeqName(gsid), loctrack.start,loctrack.end,strand);
    findTMatches(loctrack, qcount); //find matching transfrags in this xcluster
    for (int rl=0; rl < loctrack.rloci.Count(); rl++) {
       findTRMatch(loctrack, qcount, *(loctrack.rloci[rl]));
@@ -2287,7 +2445,7 @@ void xclusterLoci(int qcount, int gsid, char strand, GSeqTrack& gtrack, FILE* fl
     GList<GXLocus> xloci(false,false,false);
     buildXLoci(loctrack, qcount, gtrack, strand, &xloci);
     //the newly created xloci are in xloci
-    umrnasXStrand(xloci,qcount,gtrack);
+    umrnasXStrand(xloci, gtrack);
     //also merge these xloci into the global list of xloci
     for (int l=0; l < xloci.Count(); l++) {
        if (xloci[l]->strand=='+') {
@@ -2356,12 +2514,11 @@ void trackGData(int qcount, GList<GSeqTrack>& gtracks, GStr& fbasename, FILE** f
   FILE* f_xloci=NULL;
   int cnum=0; //consensus numbering for printITrack()
   GStr s=fbasename;
-  //this doesn't make much sense if only 1 input file was given:
-  if (qcount>1) {
+  //if (qcount>1 || generic_GFF) { //doesn't make much sense for only 1 query file
     s.append(".tracking");
     f_itrack=fopen(s.chars(),"w");
     if (f_itrack==NULL) GError("Error creating file %s !\n",s.chars());
-    }
+  //  }
   s=fbasename;
   s.append(".combined.gtf");
   f_ctrack=fopen(s.chars(),"w");
@@ -2375,8 +2532,8 @@ void trackGData(int qcount, GList<GSeqTrack>& gtracks, GStr& fbasename, FILE** f
   for (int g=0;g<gtracks.Count();g++) { //for each genomic sequence
     GSeqTrack& gseqtrack=*gtracks[g];
 
-    xclusterLoci(qcount, gseqtrack.gseq_id, '+', gseqtrack, f_ltrack);
-    xclusterLoci(qcount, gseqtrack.gseq_id, '-', gseqtrack, f_ltrack);
+    xclusterLoci(qcount,  '+', gseqtrack);
+    xclusterLoci(qcount,  '-', gseqtrack);
 
     //count XLoci, setting their id
     numXLoci(gseqtrack.xloci_f, xlocnum);
@@ -2403,14 +2560,17 @@ void trackGData(int qcount, GList<GSeqTrack>& gtracks, GStr& fbasename, FILE** f
     printXLoci(f_xloci, f_ctrack, qcount, gseqtrack.xloci_f, faseq);
     printXLoci(f_xloci, f_ctrack, qcount, gseqtrack.xloci_r, faseq);
     printXLoci(f_xloci, f_ctrack, qcount, gseqtrack.xloci_u, faseq);
-    if (tmapFiles) {
+    if (tmapFiles && haveRefs) {
       printRefMap(frs, qcount, gseqtrack.rloci_f);
       printRefMap(frs, qcount, gseqtrack.rloci_r);
       }
     delete faseq;
     }
   if (tmapFiles) {
-   for (int q=0;q<qcount;q++) { fclose(ftr[q]); fclose(frs[q]); }
+   for (int q=0;q<qcount;q++) {
+        fclose(ftr[q]); 
+        if (haveRefs) fclose(frs[q]); 
+        }
    }
   if (f_ltrack!=NULL) fclose(f_ltrack);
   if (f_itrack!=NULL) fclose(f_itrack);
diff --git a/src/cuffdiff.cpp b/src/cuffdiff.cpp
index 0820d49..3786633 100644
--- a/src/cuffdiff.cpp
+++ b/src/cuffdiff.cpp
@@ -28,6 +28,7 @@
 #include "abundances.h"
 #include "tokenize.h"
 #include "biascorrection.h"
+#include "update_check.h"
 
 #include <boost/thread.hpp>
 #include <boost/graph/adjacency_list.hpp>
@@ -38,7 +39,6 @@
 #include <boost/numeric/ublas/vector_proxy.hpp>
 #include <boost/numeric/ublas/io.hpp>
 
-#include "gtf_tracking.h"
 #include "differential.h"
 
 // Need at least this many reads in a locus to do any testing on it
@@ -52,21 +52,19 @@ using namespace boost;
 
 // We leave out the short codes for options that don't take an argument
 #if ENABLE_THREADS
-const char *short_options = "m:p:s:F:c:I:j:Q:L:M:o:r:TNqv";
+const char *short_options = "m:p:s:c:I:j:L:M:o:b:TNqvuF:";
 #else
-const char *short_options = "m:s:F:c:I:j:Q:L:M:o:r:TNqv";
+const char *short_options = "m:s:c:I:j:L:M:o:b:TNqvuF:";
 #endif
 
 
 
 static struct option long_options[] = {
-{"inner-dist-mean",			required_argument,       0,          'm'},
-{"inner-dist-stddev",		required_argument,       0,          's'},
+{"frag-len-mean",			required_argument,       0,          'm'},
+{"frag-len-std-dev",			required_argument,       0,          's'},
 {"transcript-score-thresh", required_argument,       0,          't'},
-{"min-isoform-fraction",    required_argument,       0,          'F'},
 {"pre-mrna-fraction",		required_argument,		 0,			 'j'},
 {"max-intron-length",		required_argument,		 0,			 'I'},
-{"min-map-qual",			required_argument,		 0,			 'Q'},
 {"labels",					required_argument,		 0,			 'L'},
 {"min-alignment-count",     required_argument,		 0,			 'c'},
 {"FDR",					    required_argument,		 0,			 OPT_FDR},
@@ -74,18 +72,26 @@ static struct option long_options[] = {
 {"output-dir",			    required_argument,		 0,			 'o'},
 {"verbose",			    	no_argument,			 0,			 'v'},
 {"quiet",			    	no_argument,			 0,			 'q'},
-{"reference-seq",			required_argument,		 0,			 'r'},
+{"frag-bias-correct",       required_argument,		 0,			 'b'},
+{"multi-read-correct",      no_argument,			 0,			 'u'},
 {"time-series",             no_argument,             0,			 'T'},
-{"quartile-normalization",  no_argument,	 		 0,	         'N'},
+{"upper-quartile-norm",     no_argument,	 		 0,	         'N'},
+{"min-isoform-fraction",    required_argument,       0,          'F'},
 #if ENABLE_THREADS
 {"num-threads",				required_argument,       0,          'p'},
 #endif
 {"library-type",		    required_argument,		 0,			 OPT_LIBRARY_TYPE},
+{"seed",                    required_argument,		 0,			 OPT_RANDOM_SEED},
+{"no-collapse-cond-prob",   no_argument,             0,			 OPT_COLLAPSE_COND_PROB},
 {"num-importance-samples",  required_argument,		 0,			 OPT_NUM_IMP_SAMPLES},
-{"max-mle-iterations",		 required_argument,		 0,			 OPT_MLE_MAX_ITER},
-#if ADAM_MODE
-{"bias-mode",		    required_argument,		 0,			 OPT_BIAS_MODE},
-#endif
+{"max-mle-iterations",		required_argument,		 0,			 OPT_MLE_MAX_ITER},
+{"poisson-dispersion",		no_argument,             0,		     OPT_POISSON_DISPERSION},
+{"bias-mode",               required_argument,		 0,			 OPT_BIAS_MODE},
+{"no-update-check",         no_argument,             0,          OPT_NO_UPDATE_CHECK},
+{"emit-count-tables",       no_argument,             0,          OPT_EMIT_COUNT_TABLES},
+{"compatible-hits-norm",    no_argument,	 		 0,	         OPT_USE_COMPAT_MASS},
+{"total-hits-norm",         no_argument,	 		 0,	         OPT_USE_TOTAL_MASS},
+
 {0, 0, 0, 0} // terminator
 };
 
@@ -97,27 +103,32 @@ void print_usage()
 	//NOTE: SPACES ONLY, bozo
     fprintf(stderr, "Usage:   cuffdiff [options] <transcripts.gtf> <sample1_hits.sam> <sample2_hits.sam> [... sampleN_hits.sam]\n");
 	fprintf(stderr, "   Supply replicate SAMs as comma separated lists for each condition: sample1_rep1.sam,sample1_rep2.sam,...sample1_repM.sam\n");
-    fprintf(stderr, "Options:\n\n");
+    fprintf(stderr, "General Options:\n");
+    fprintf(stderr, "  -o/--output-dir              write all output files to this directory              [ default:     ./ ]\n");
     fprintf(stderr, "  -T/--time-series             treat samples as a time-series                        [ default:  FALSE ]\n");
-   	fprintf(stderr, "  -N/--quartile-normalization  use upper-quartile normalization                      [ default:  FALSE ]\n");
-	fprintf(stderr, "  -Q/--min-map-qual            ignore alignments with lower than this mapping qual   [ default:      0 ]\n");
-	fprintf(stderr, "  -c/--min-alignment-count     minimum number of alignments in a locus for testing   [ default:   1000 ]\n");
+	fprintf(stderr, "  -c/--min-alignment-count     minimum number of alignments in a locus for testing   [ default:   10 ]\n");
 	fprintf(stderr, "  --FDR                        False discovery rate used in testing                  [ default:   0.05 ]\n");
 	fprintf(stderr, "  -M/--mask-file               ignore all alignment within transcripts in this file  [ default:   NULL ]\n");
-    fprintf(stderr, "  -v/--verbose                 log-friendly verbose processing (no progress bar)     [ default:  FALSE ]\n");
-	fprintf(stderr, "  -q/--quiet                   log-friendly quiet processing (no progress bar)       [ default:  FALSE ]\n");
-	fprintf(stderr, "  -o/--output-dir              write all output files to this directory              [ default:     ./ ]\n");
-	fprintf(stderr, "  -r/--reference-seq           reference fasta file for sequence bias correction     [ default:   NULL ]\n");
+    fprintf(stderr, "  -b/--frag-bias-correct       use bias correction - reference fasta required        [ default:   NULL ]\n");
+    fprintf(stderr, "  -u/--multi-read-correct      use 'rescue method' for multi-reads (more accurate)   [ default:  FALSE ]\n");
+    fprintf(stderr, "  -N/--upper-quartile-norm     use upper-quartile normalization                      [ default:  FALSE ]\n");
     fprintf(stderr, "  -L/--labels                  comma-separated list of condition labels\n");
 #if ENABLE_THREADS
 	fprintf(stderr, "  -p/--num-threads             number of threads used during quantification          [ default:      1 ]\n");
 #endif
-	fprintf(stderr, "\nAdvanced Options:\n\n");
+	fprintf(stderr, "\nAdvanced Options:\n");
     fprintf(stderr, "  --library-type               Library prep used for input reads                     [ default:  below ]\n");
-    fprintf(stderr, "  -m/--frag-len-mean           the average fragment length (use with unpaired reads only)               \n");
-	fprintf(stderr, "  -s/--frag-len-std-dev        the fragment length standard deviation  (use with unpaired reads only)   \n");
-	fprintf(stderr, "  --num-importance-samples     number of importance samples for MAP restimation      [ default:   1000 ]\n");
+    fprintf(stderr, "  -m/--frag-len-mean           average fragment length (unpaired reads only)         [ default:    200 ]\n");
+    fprintf(stderr, "  -s/--frag-len-std-dev        fragment length std deviation (unpaired reads only)   [ default:     80 ]\n");
+    fprintf(stderr, "  --num-importance-samples     number of importance samples for MAP restimation      [ default:   1000 ]\n");
 	fprintf(stderr, "  --max-mle-iterations         maximum iterations allowed for MLE calculation        [ default:   5000 ]\n");
+    fprintf(stderr, "  --compatible-hits-norm       count hits compatible with reference RNAs only        [ default:  TRUE  ]\n");
+    fprintf(stderr, "  --total-hits-norm            count all hits for normalization                      [ default:  FALSE ]\n");
+    fprintf(stderr, "  --poisson-dispersion         Don't fit fragment counts for overdispersion          [ default:  FALSE ]\n");
+    fprintf(stderr, "  -v/--verbose                 log-friendly verbose processing (no progress bar)     [ default:  FALSE ]\n");
+	fprintf(stderr, "  -q/--quiet                   log-friendly quiet processing (no progress bar)       [ default:  FALSE ]\n");
+    fprintf(stderr, "  --no-update-check            do not contact server to check for update availability[ default:  FALSE ]\n");    
+    fprintf(stderr, "  --emit-count-tables          print count tables used to fit overdispersion         [ default:  FALSE ]\n");    
     print_library_table();
 }
 
@@ -139,17 +150,22 @@ int parse_options(int argc, char** argv)
                 break;
                 
 			case 'm':
+				user_provided_fld = true;
 				def_frag_len_mean = (uint32_t)parseInt(0, "-m/--frag-len-mean arg must be at least 0", print_usage);
 				break;
 			case 'c':
 				min_read_count = (uint32_t)parseInt(0, "-c/--min-alignment-count arg must be at least 0", print_usage);
 				break;
 			case 's':
+				user_provided_fld = true;
 				def_frag_len_std_dev = (uint32_t)parseInt(0, "-s/--frag-len-std-dev arg must be at least 0", print_usage);
 				break;
 			case 'p':
 				num_threads = (uint32_t)parseInt(1, "-p/--num-threads arg must be at least 1", print_usage);
 				break;
+            case 'F':
+				min_isoform_fraction = parseFloat(0, 1.0, "-F/--min-isoform-fraction must be between 0 and 1.0", print_usage);
+				break;
             case 'L':
 				sample_label_list = optarg;
 				break;
@@ -163,22 +179,22 @@ int parse_options(int argc, char** argv)
 				max_mle_iterations = parseInt(1, "--max-mle-iterations must be at least 1", print_usage);
 				break;
 			case OPT_BIAS_MODE:
-				bias_mode = optarg;
-				break;
-			case 'Q':
-			{
-				int min_map_qual = parseInt(0, "-Q/--min-map-qual must be at least 0", print_usage);
-				if (min_map_qual > 0)
-				{
-					long double p = (-1.0 * min_map_qual) / 10.0;
-					max_phred_err_prob = pow(10.0L, p);
-				}
+				if (!strcmp(optarg, "site"))
+					bias_mode = SITE;
+				else if (!strcmp(optarg, "pos"))
+					bias_mode = POS;
+				else if (!strcmp(optarg, "pos_vlmm"))
+					bias_mode = POS_VLMM;
+				else if (!strcmp(optarg, "vlmm"))
+					bias_mode = VLMM;
+                else if (!strcmp(optarg, "pos_site"))
+					bias_mode = POS_SITE;
 				else
 				{
-					max_phred_err_prob = 1.0;
+					fprintf(stderr, "Unknown bias mode.\n");
+					exit(1);
 				}
 				break;
-			}
 			case 'M':
 			{
 				mask_gtf_filename = optarg;
@@ -209,7 +225,7 @@ int parse_options(int argc, char** argv)
 				output_dir = optarg;
 				break;
 			}
-			case 'r':
+			case 'b':
 			{
 				fasta_dir = optarg;
 				corr_bias = true;
@@ -226,18 +242,82 @@ int parse_options(int argc, char** argv)
             	use_quartile_norm = true;
             	break;
             }
+            case 'u':
+            {
+                corr_multi = true;
+                break;
+            }
             case OPT_LIBRARY_TYPE:
 			{
 				library_type = optarg;
 				break;
 			}
-
+            case OPT_POISSON_DISPERSION:
+			{
+				poisson_dispersion = true;
+				break;
+			}
+            case OPT_NO_UPDATE_CHECK:
+            {
+                no_update_check = true;
+                break;
+            }
+            case OPT_RANDOM_SEED:
+            {
+                random_seed = parseInt(0, "--seed must be at least 0", print_usage);
+                break;
+            }
+            case OPT_EMIT_COUNT_TABLES:
+            {
+                emit_count_tables = true;
+                break;
+            }    
+            case OPT_COLLAPSE_COND_PROB:
+            {
+                cond_prob_collapse = false;
+                break;
+            }
+            case OPT_USE_COMPAT_MASS:
+            {
+                use_compat_mass = true;
+                break;
+            }
+            case OPT_USE_TOTAL_MASS:
+            {
+                use_total_mass = true;
+                break;
+            }
 			default:
 				print_usage();
 				return 1;
         }
     } while(next_option != -1);
 	
+	if (library_type != "")
+    {
+        map<string, ReadGroupProperties>::iterator lib_itr = 
+		library_type_table.find(library_type);
+        if (lib_itr == library_type_table.end())
+        {
+            fprintf(stderr, "Error: Library type %s not supported\n", library_type.c_str());
+            exit(1);
+        }
+        else 
+        {
+            if (library_type == "transfrags")
+            {
+                allow_junk_filtering = false;
+            }
+            global_read_properties = &lib_itr->second;
+        }
+    }
+    
+    if (use_total_mass && use_compat_mass)
+    {
+        fprintf (stderr, "Error: please supply only one of --compatibile-hits-norm and --total-hits-norm\n");
+        exit(1);
+    }
+    
     tokenize(sample_label_list, ",", sample_labels);
     
 	allow_junk_filtering = false;
@@ -245,44 +325,6 @@ int parse_options(int argc, char** argv)
 	return 0;
 }
 
-
-#if ENABLE_THREADS
-mutex locus_thread_pool_lock;
-int locus_curr_threads = 0;
-int locus_num_threads = 0;
-
-void decr_pool_count()
-{
-	locus_thread_pool_lock.lock();
-	locus_curr_threads--;
-	locus_thread_pool_lock.unlock();	
-}
-#endif
-
-template<typename T>
-string cat_strings(const T& container, const char* delimiter=",")
-{
-	string cat;
-	if (container.empty())
-	{
-		cat = "";
-	}
-	else
-	{
-		typename T::const_iterator itr = container.begin();
-		//cat = *(itr);
-		for (; itr != container.end(); itr++)
-		{
-			if (!(*itr).empty()) {
-				if (!cat.empty()) cat += delimiter;
-				cat += *itr; 
-				}
-		}
-	}
-
-	return cat;
-}
-
 void print_tests(FILE* fout,
                  const char* sample_1_label,
                  const char* sample_2_label,
@@ -293,19 +335,24 @@ void print_tests(FILE* fout,
 		 ++itr)
 	{
 		const SampleDifference& test = itr->second;
-		
-		string all_gene_names = cat_strings(test.gene_names);
+        
+        string all_gene_ids = cat_strings(test.meta_data->gene_ids);
+		if (all_gene_ids == "")
+			all_gene_ids = "-";
+        
+		string all_gene_names = cat_strings(test.meta_data->gene_names);
 		if (all_gene_names == "")
 			all_gene_names = "-";
 		
-		string all_protein_ids = cat_strings(test.protein_ids);	
+		string all_protein_ids = cat_strings(test.meta_data->protein_ids);	
 		if (all_protein_ids == "")
 			all_protein_ids = "-";
 		
-		fprintf(fout, "%s\t%s\t%s\t%s\t%s", 
+		fprintf(fout, "%s\t%s\t%s\t%s\t%s\t%s", 
                 itr->first.c_str(), 
+                all_gene_ids.c_str(),
                 all_gene_names.c_str(), 
-                test.locus_desc.c_str(),
+                test.meta_data->locus_desc.c_str(),
                 sample_1_label,
                 sample_2_label);
 		
@@ -316,6 +363,7 @@ void print_tests(FILE* fout,
 			double r2 = test.value_2;
 			double d = test.differential;
 			double p = test.p_value;
+			double q = test.corrected_p;
 			const char* sig;
 			if (test.significant && test.test_status == OK)
 				sig = "yes";
@@ -325,15 +373,19 @@ void print_tests(FILE* fout,
 			const char* status;
 			if (test.test_status == OK)
 				status = "OK";
-			else
+			else if (test.test_status == LOWDATA)
+                status = "LOWDATA";
+            else if (test.test_status == NOTEST)
 				status = "NOTEST";
+            else
+                assert(false);
 			
-			fprintf(fout, "\t%s\t%lg\t%lg\t%lg\t%lg\t%lg\t%s", status, r1, r2, d, t, p, sig);
+			fprintf(fout, "\t%s\t%lg\t%lg\t%lg\t%lg\t%lg\t%lg\t%s", status, r1, r2, d, t, p, q, sig);
 			fprintf(fout, "\n");
 		}
 		else
 		{
-			fprintf(fout, "\tFAIL\t0.0\t0.0\t0.0\t0.0\t1.0\tno\n");
+			fprintf(fout, "\tFAIL\t0.0\t0.0\t0.0\t0.0\t1.0\t1.0\tno\n");
 		}
 	}
 }
@@ -341,7 +393,7 @@ void print_tests(FILE* fout,
 void print_FPKM_tracking(FILE* fout, 
 						 const FPKMTrackingTable& tracking)
 {
-	fprintf(fout,"tracking_id\tclass_code\tnearest_ref_id\tgene_short_name\ttss_id\tlocus");
+	fprintf(fout,"tracking_id\tclass_code\tnearest_ref_id\tgene_id\tgene_short_name\ttss_id\tlocus\tlength\tcoverage\tstatus");
 	FPKMTrackingTable::const_iterator first_itr = tracking.begin();
 	if (first_itr != tracking.end())
 	{
@@ -359,6 +411,17 @@ void print_FPKM_tracking(FILE* fout,
 		const FPKMTracking& track = itr->second;
 		const vector<FPKMContext>& fpkms = track.fpkm_series;
 		
+        AbundanceStatus status = NUMERIC_OK;
+        foreach (const FPKMContext& c, fpkms)
+        {
+            if (c.status == NUMERIC_FAIL)
+                status = NUMERIC_FAIL;
+        }
+        
+        string all_gene_ids = cat_strings(track.gene_ids);
+		if (all_gene_ids == "")
+			all_gene_ids = "-";
+        
 		string all_gene_names = cat_strings(track.gene_names);
 		if (all_gene_names == "")
 			all_gene_names = "-";
@@ -367,14 +430,22 @@ void print_FPKM_tracking(FILE* fout,
 		if (all_tss_ids == "")
 			all_tss_ids = "-";
 		
-		fprintf(fout, "%s\t%c\t%s\t%s\t%s\t%s", 
-				description.c_str(),
-				track.classcode ? track.classcode : '-',
-				track.ref_match.c_str(),
-				all_gene_names.c_str(), 
-				all_tss_ids.c_str(),
-				track.locus_tag.c_str());
-		
+        char length_buff[33] = "-";
+        if (track.length)
+            sprintf(length_buff, "%d", track.length);
+        
+        fprintf(fout, "%s\t%c\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s", 
+                description.c_str(),
+                track.classcode ? track.classcode : '-',
+                track.ref_match.c_str(),
+                all_gene_ids.c_str(),
+                all_gene_names.c_str(), 
+                all_tss_ids.c_str(),
+                track.locus_tag.c_str(),
+                length_buff,
+                "-",
+                status == NUMERIC_FAIL ? "FAIL" : "OK");
+       		
 		for (size_t i = 0; i < fpkms.size(); ++i)
 		{
 			double fpkm = fpkms[i].FPKM;
@@ -411,13 +482,16 @@ int fdr_significance(double fdr,
 			tests[k]->significant = false;
 		}
 	}
-	
+	int significant = 0;
 	for (int k = 0; k < (int)passing.size(); ++k)
 	{
-		double r = (double)passing.size() / (k + 1);
+		double r = (double)passing.size() / ((double) k + 1);
 		double corrected_p = passing[k]->p_value * r;
+		passing[k]->corrected_p = corrected_p;
 		passing[k]->significant = (corrected_p <= fdr);
+        significant += passing[k]->significant;
 	}
+    
 	return passing.size();
 }
 
@@ -433,7 +507,7 @@ void extract_sample_diffs(SampleDiffs& diff_map,
 }
 
 #if ENABLE_THREADS
-mutex inspect_lock;
+boost::mutex inspect_lock;
 #endif
 
 void inspect_map_worker(ReplicatedBundleFactory& fac,
@@ -468,26 +542,25 @@ void learn_bias_worker(shared_ptr<BundleFactory> fac)
 	BiasLearner* bl = new BiasLearner(rg_props->frag_len_dist());
 	learn_bias(*fac, *bl, false);
 	rg_props->bias_learner(shared_ptr<BiasLearner const>(bl));
-	fac->reset();
 }
 
+
+shared_ptr<TestLauncher> test_launcher;
+
 bool quantitate_next_locus(const RefSequenceTable& rt,
-                           vector<ReplicatedBundleFactory>& bundle_factories,
-                           vector<shared_ptr<SampleAbundances> >& abundances)
+                           vector<shared_ptr<ReplicatedBundleFactory> >& bundle_factories,
+                           shared_ptr<TestLauncher> launcher)
 {
-    vector<shared_ptr<bool> > non_empty_bundle_flags;
-    
     for (size_t i = 0; i < bundle_factories.size(); ++i)
     {
-        shared_ptr<bool> sample_non_empty = shared_ptr<bool>(new bool);
         shared_ptr<SampleAbundances> s_ab = shared_ptr<SampleAbundances>(new SampleAbundances);
+        
 #if ENABLE_THREADS					
         while(1)
         {
             locus_thread_pool_lock.lock();
             if (locus_curr_threads < locus_num_threads)
             {
-                locus_thread_pool_lock.unlock();
                 break;
             }
             
@@ -497,67 +570,36 @@ bool quantitate_next_locus(const RefSequenceTable& rt,
             
         }
         
-        locus_thread_pool_lock.lock();
         locus_curr_threads++;
         locus_thread_pool_lock.unlock();
         
         thread quantitate(sample_worker,
                           boost::ref(rt),
-                          boost::ref(bundle_factories[i]),
+                          boost::ref(*(bundle_factories[i])),
                           s_ab,
-                          sample_non_empty);  
+                          i,
+                          launcher);  
 #else
         sample_worker(boost::ref(rt),
-                      boost::ref(bundle_factories[i]),
+                      boost::ref(*(bundle_factories[i])),
                       s_ab,
-                      sample_non_empty);
+                      i,
+                      launcher);
 #endif
-		
-        abundances.push_back(s_ab);
-        non_empty_bundle_flags.push_back(sample_non_empty);
     }
-    
-    // wait for the workers to finish up before doing the cross-sample testing.
-#if ENABLE_THREADS	
-    while(1)
-    {
-        locus_thread_pool_lock.lock();
-        if (locus_curr_threads == 0)
-        {
-            locus_thread_pool_lock.unlock();
-            break;
-        }
-        
-        locus_thread_pool_lock.unlock();
-        
-        boost::this_thread::sleep(boost::posix_time::milliseconds(5));
-        
-    }
-#endif
-    
-    bool more_loci_remain = false;
-    foreach (shared_ptr<bool> sample_flag, non_empty_bundle_flags)
-    {
-        if (*sample_flag)
-        {
-            more_loci_remain = true;
-            break;
-        }
-    }
-    
-    // There's no more loci to be processed.
-    return more_loci_remain;
-
+    return true;
 }
 
 void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_lists, Outfiles& outfiles)
 {
+
 	ReadTable it;
 	RefSequenceTable rt(true, false);
     
 	vector<shared_ptr<Scaffold> > ref_mRNAs;
 	
-	vector<ReplicatedBundleFactory> bundle_factories;    
+	vector<shared_ptr<ReplicatedBundleFactory> > bundle_factories;
+    vector<shared_ptr<ReadGroupProperties> > all_read_groups;
     vector<shared_ptr<HitFactory> > all_hit_factories;
     
 	for (size_t i = 0; i < sam_hit_filename_lists.size(); ++i)
@@ -591,7 +633,7 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
             
             all_hit_factories.push_back(hs);
             
-            shared_ptr<BundleFactory> hf(new BundleFactory(hs));
+            shared_ptr<BundleFactory> hf(new BundleFactory(hs, REF_DRIVEN));
             shared_ptr<ReadGroupProperties> rg_props(new ReadGroupProperties);
             
             if (global_read_properties)
@@ -603,14 +645,16 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
                 *rg_props = hs->read_group_properties();
             }
             
+            all_read_groups.push_back(rg_props);
+            
             hf->read_group_properties(rg_props);
             
             replicate_factories.push_back(hf);
             //replicate_factories.back()->set_ref_rnas(ref_mRNAs);
         }
         
-        ReplicatedBundleFactory rep_factory(replicate_factories);
-        bundle_factories.push_back(rep_factory);
+        string condition_name = sample_labels[i];
+        bundle_factories.push_back(shared_ptr<ReplicatedBundleFactory>(new ReplicatedBundleFactory(replicate_factories, condition_name)));
 	}
     
     ::load_ref_rnas(ref_gtf, rt, ref_mRNAs, corr_bias, false);
@@ -623,10 +667,11 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
         ::load_ref_rnas(mask_gtf, rt, mask_rnas, false, false);
     }
     
-    foreach (ReplicatedBundleFactory& fac, bundle_factories)
+    foreach (shared_ptr<ReplicatedBundleFactory> fac, bundle_factories)
     {
-        fac.set_ref_rnas(ref_mRNAs);
-        if (mask_gtf) fac.set_mask_rnas(mask_rnas);
+        fac->set_ref_rnas(ref_mRNAs);
+        if (mask_gtf) 
+            fac->set_mask_rnas(mask_rnas);
     }
     
 #if ENABLE_THREADS
@@ -637,7 +682,7 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
 	int tmp_max_frag_len = 0;
 	
 	ProgressBar p_bar("Inspecting maps and determining fragment length distributions.",0);
-	foreach (ReplicatedBundleFactory& fac, bundle_factories)
+	foreach (shared_ptr<ReplicatedBundleFactory> fac, bundle_factories)
     {
 #if ENABLE_THREADS	
         while(1)
@@ -645,7 +690,6 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
             locus_thread_pool_lock.lock();
             if (locus_curr_threads < locus_num_threads)
             {
-                locus_thread_pool_lock.unlock();
                 break;
             }
             
@@ -653,16 +697,16 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
             
             boost::this_thread::sleep(boost::posix_time::milliseconds(5));
         }
-        locus_thread_pool_lock.lock();
+        
         locus_curr_threads++;
         locus_thread_pool_lock.unlock();
         
         thread inspect(inspect_map_worker,
-                       boost::ref(fac),
+                       boost::ref(*fac),
                        boost::ref(tmp_min_frag_len),
                        boost::ref(tmp_max_frag_len));  
 #else
-        inspect_map_worker(boost::ref(fac),
+        inspect_map_worker(boost::ref(*fac),
                            boost::ref(tmp_min_frag_len),
                            boost::ref(tmp_max_frag_len));
 #endif
@@ -678,40 +722,200 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
             locus_thread_pool_lock.unlock();
             break;
         }
-        
         locus_thread_pool_lock.unlock();
         
         boost::this_thread::sleep(boost::posix_time::milliseconds(5));
     }
 #endif
+    
+    int most_reps = -1;
+    int most_reps_idx = 0;
+    
+    bool single_replicate_fac = false;
+    
+    for (size_t i = 0; i < bundle_factories.size(); ++i)
+    {
+        ReplicatedBundleFactory& fac = *(bundle_factories[i]);
+        if (fac.num_replicates() > most_reps)
+        {
+            most_reps = fac.num_replicates();
+            most_reps_idx = i;
+        }
+        if (most_reps == 1)
+        {
+            single_replicate_fac = true;
+        }
+    }
+    
+    if (most_reps != 1 && poisson_dispersion == false)
+    {
+        foreach (shared_ptr<ReplicatedBundleFactory> fac, bundle_factories)
+        {
+            if (fac->num_replicates() == 1)
+            {
+                fac->mass_dispersion_model(bundle_factories[most_reps_idx]->mass_dispersion_model());
+            }
+        }
+    }
+    
+    if (most_reps == 1 && poisson_dispersion == false)
+    {
+        vector<pair<string, vector<double> > > sample_count_table;
+        for (size_t i = 0; i < all_read_groups.size(); ++i)
+        {
+            shared_ptr<ReadGroupProperties> rg_props = all_read_groups[i];
+            const vector<pair<string, double> >& common_count_table = rg_props->common_scale_counts();
+            double unscaling_factor = 1.0 / rg_props->mass_scale_factor();
+            for (size_t j = 0; j < common_count_table.size(); ++j)
+            {
+                if (sample_count_table.size() == j)
+                {
+                    const string& locus_id = common_count_table[j].first;
+                    sample_count_table.push_back(make_pair(locus_id, vector<double>(all_read_groups.size(), 0.0)));
+                }
+                double scaled = common_count_table[j].second;
+                sample_count_table[j].second[i] = scaled * unscaling_factor;
+                assert(sample_count_table[j].second[i] >= 0 && !isinf(sample_count_table[j].second[i]));
+            }
+        }
+        
+        vector<double> scale_factors(all_read_groups.size(), 0.0);
+        
+        // TODO: needs to be refactored - similar code exists in replicates.cpp
+        calc_scaling_factors(sample_count_table, scale_factors);
+        
+        for (size_t i = 0; i < all_read_groups.size(); ++i)
+        {
+            shared_ptr<ReadGroupProperties> rg_props = all_read_groups[i];
+            rg_props->mass_scale_factor(scale_factors[i]);
+        }
+        
+        // Transform raw counts to the common scale
+        for (size_t i = 0; i < sample_count_table.size(); ++i)
+        {
+            pair<string, vector<double> >& p = sample_count_table[i];
+            for (size_t j = 0; j < p.second.size(); ++j)
+            {
+                assert (scale_factors.size() > j);
+                p.second[j] *= (1.0 / scale_factors[j]);
+            }
+        }
+        
+        for (size_t i = 0; i < all_read_groups.size(); ++i)
+        {
+            shared_ptr<ReadGroupProperties> rg_props = all_read_groups[i];
+            vector<pair<string, double> > scaled_counts;
+            for (size_t j = 0; j < sample_count_table.size(); ++j)
+            {
+                pair<string, double> counts;
+                string& locus_id = sample_count_table[j].first;
+                double locus_count = sample_count_table[j].second[i];
+                counts = make_pair(locus_id, locus_count);
+                scaled_counts.push_back(counts);
+            }
+            rg_props->common_scale_counts(scaled_counts);
+            // revert each read group back to native scaling to avoid a systematic fold change toward the mean.
+
+            rg_props->mass_scale_factor(1.0);         
+        }
+        
+        shared_ptr<MassDispersionModel const> disperser;
+        disperser = fit_dispersion_model("pooled", scale_factors, sample_count_table);
+
+        foreach (shared_ptr<ReadGroupProperties> rg_props, all_read_groups)
+        {
+            rg_props->mass_dispersion_model(disperser);
+        }
+
+    }
+
+    long double total_norm_mass = 0.0;
+    long double total_mass = 0.0;
+    foreach (shared_ptr<ReadGroupProperties> rg_props, all_read_groups)
+    {
+        total_norm_mass += rg_props->normalized_map_mass();
+        total_mass += rg_props->total_map_mass();
+    }
+    
+    // scale the normalized masses so that both quantile total count normalization
+    // are roughly on the same numerical scale
+    foreach (shared_ptr<ReadGroupProperties> rg_props, all_read_groups)
+    {
+        long double new_norm = rg_props->normalized_map_mass() * (total_mass / total_norm_mass);
+        rg_props->normalized_map_mass(new_norm);
+    }
 
 	min_frag_len = tmp_min_frag_len;
     max_frag_len = tmp_max_frag_len;
 	
 	final_est_run = false;
 	
-	int num_bundles = bundle_factories[0].num_bundles();
+	double num_bundles = (double)bundle_factories[0]->num_bundles();
 	
-	if (corr_bias) // Only run initial estimation if correcting bias
+    //test_launcher = shared_ptr<TestLauncher>(new TestLauncher(bundle_factories.size(), &tests, &tracking, samples_are_time_series, p_bar)
+    
+	if (corr_bias || corr_multi) // Only run initial estimation if correcting bias or multi-reads
 	{
-		p_bar = ProgressBar("Calculating initial abundance estimates.", num_bundles);
-		
+        if (corr_bias && corr_multi)
+            p_bar = ProgressBar("Calculating initial abundance estimates for bias and multi-read correction.", num_bundles);
+        else if (corr_bias)
+            p_bar = ProgressBar("Calculating initial abundance estimates for bias correction.", num_bundles);
+        else if (corr_multi)
+            p_bar = ProgressBar("Calculating initial abundance estimates for multi-read correction.", num_bundles);
+
 		while (1) 
 		{
-			p_bar.update("",1);
-			vector<shared_ptr<SampleAbundances> > abundances;
-			bool more_loci_remain = quantitate_next_locus(rt, bundle_factories, abundances);
-			
+			//p_bar.update("",1);
+            test_launcher = shared_ptr<TestLauncher>(new TestLauncher((int)bundle_factories.size(), NULL, NULL, samples_are_time_series, &p_bar));
+                                                     
+			shared_ptr<vector<shared_ptr<SampleAbundances> > > abundances(new vector<shared_ptr<SampleAbundances> >());
+			quantitate_next_locus(rt, bundle_factories, test_launcher);
+			bool more_loci_remain = false;
+            foreach (shared_ptr<ReplicatedBundleFactory> rep_fac, bundle_factories) 
+            {
+                if (rep_fac->bundles_remain())
+                {
+                    more_loci_remain = true;
+                    break;
+                }
+            }
+            
 			if (!more_loci_remain)
+            {
+                // wait for the workers to finish up before breaking out.
+#if ENABLE_THREADS	
+                while(1)
+                {
+                    locus_thread_pool_lock.lock();
+                    if (locus_curr_threads == 0)
+                    {
+                        locus_thread_pool_lock.unlock();
+                        break;
+                    }
+                    
+                    locus_thread_pool_lock.unlock();
+                    
+                    boost::this_thread::sleep(boost::posix_time::milliseconds(5));
+                    
+                }
+#endif
 				break;
+            }
 		}
+        
+        foreach (shared_ptr<ReplicatedBundleFactory> rep_fac, bundle_factories)
+		{
+			rep_fac->reset();
+        }
+        
 		p_bar.complete();
-	
-		p_bar = ProgressBar("Learning bias parameters.", 0);
-		foreach (ReplicatedBundleFactory& rep_fac, bundle_factories)
+	}
+    if (corr_bias)
+    {
+        p_bar = ProgressBar("Learning bias parameters.", 0);
+		foreach (shared_ptr<ReplicatedBundleFactory> rep_fac, bundle_factories)
 		{
-			rep_fac.reset();
-			foreach (shared_ptr<BundleFactory> fac, rep_fac.factories())
+			foreach (shared_ptr<BundleFactory> fac, rep_fac->factories())
 			{
 #if ENABLE_THREADS	
 				while(1)
@@ -719,7 +923,6 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
 					locus_thread_pool_lock.lock();
 					if (locus_curr_threads < locus_num_threads)
 					{
-						locus_thread_pool_lock.unlock();
 						break;
 					}
 					
@@ -727,7 +930,6 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
 					
 					boost::this_thread::sleep(boost::posix_time::milliseconds(5));
 				}
-				locus_thread_pool_lock.lock();
 				locus_curr_threads++;
 				locus_thread_pool_lock.unlock();
 				
@@ -754,7 +956,13 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
 			boost::this_thread::sleep(boost::posix_time::milliseconds(5));
 		}
 #endif
+        foreach (shared_ptr<ReplicatedBundleFactory> rep_fac, bundle_factories)
+		{
+			rep_fac->reset();
+        }
 	}
+    
+    
 	
 	Tests tests;
     
@@ -783,39 +991,44 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
 	
 	final_est_run = true;
 	p_bar = ProgressBar("Testing for differential expression and regulation in locus.", num_bundles);
+                                                     
+    test_launcher = shared_ptr<TestLauncher>(new TestLauncher(bundle_factories.size(), &tests, &tracking, samples_are_time_series, &p_bar));
+                                                                                              
 	while (true)
 	{
-        vector<shared_ptr<SampleAbundances> > abundances;
-        bool more_loci_remain = quantitate_next_locus(rt, bundle_factories, abundances);
-        
+        //shared_ptr<vector<shared_ptr<SampleAbundances> > > abundances(new vector<shared_ptr<SampleAbundances> >());
+        quantitate_next_locus(rt, bundle_factories, test_launcher);
+        bool more_loci_remain = false;
+        foreach (shared_ptr<ReplicatedBundleFactory> rep_fac, bundle_factories) 
+        {
+            if (rep_fac->bundles_remain())
+            {
+                more_loci_remain = true;
+                break;
+            }
+        }
         if (!more_loci_remain)
-            break;
-        
-        // TODO: more asserts qhere to verify that transcripts tested are
-        // identical, etc.
-        
-        for (size_t i = 1; i < abundances.size(); ++i)
         {
-            const SampleAbundances& curr = *(abundances[i]);
-            const SampleAbundances& prev = *(abundances[i-1]);
-
-            assert (curr.locus_tag == prev.locus_tag);
-            
-            const AbundanceGroup& s1 = curr.transcripts;
-            const AbundanceGroup& s2 =  prev.transcripts;
-            
-            assert (s1.abundances().size() == s2.abundances().size());
-            
-            for (size_t j = 0; j < s1.abundances().size(); ++j)
+            // wait for the workers to finish up before doing the cross-sample testing.
+#if ENABLE_THREADS	
+            while(1)
             {
-                assert (s1.abundances()[j]->description() == s1.abundances()[j]->description());
+                locus_thread_pool_lock.lock();
+                if (locus_curr_threads == 0)
+                {
+                    locus_thread_pool_lock.unlock();
+                    break;
+                }
+                
+                locus_thread_pool_lock.unlock();
+                
+                boost::this_thread::sleep(boost::posix_time::milliseconds(5));
+                
             }
+#endif
+            break;
         }
-
-        verbose_msg("Testing for differential expression and regulation in locus [%s]\n", abundances.front()->locus_tag.c_str());
-		p_bar.update(abundances.front()->locus_tag.c_str(), 1);
-		test_differential(abundances.front()->locus_tag, abundances, tests, tracking, samples_are_time_series);
-	}
+    }
 	
 	p_bar.complete();
 
@@ -833,7 +1046,7 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
 	}
 	int iso_exp_tests = fdr_significance(FDR, isoform_exp_diffs);
 	fprintf(stderr, "Performed %d isoform-level transcription difference tests\n", iso_exp_tests);
-    fprintf(outfiles.isoform_de_outfile, "test_id\tgene\tlocus\tsample_1\tsample_2\tstatus\tvalue_1\tvalue_2\tln(fold_change)\ttest_stat\tp_value\tsignificant\n");
+    fprintf(outfiles.isoform_de_outfile, "test_id\tgene_id\tgene\tlocus\tsample_1\tsample_2\tstatus\tvalue_1\tvalue_2\tln(fold_change)\ttest_stat\tp_value\tq_value\tsignificant\n");
 	for (size_t i = 1; i < tests.isoform_de_tests.size(); ++i)
 	{
         for (size_t j = 0; j < i; ++j)
@@ -855,7 +1068,7 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
 	
 	int tss_group_exp_tests = fdr_significance(FDR, tss_group_exp_diffs);
 	fprintf(stderr, "Performed %d tss-level transcription difference tests\n", tss_group_exp_tests);
-    fprintf(outfiles.group_de_outfile, "test_id\tgene\tlocus\tsample_1\tsample_2\tstatus\tvalue_1\tvalue_2\tln(fold_change)\ttest_stat\tp_value\tsignificant\n");
+    fprintf(outfiles.group_de_outfile, "test_id\tgene_id\tgene\tlocus\tsample_1\tsample_2\tstatus\tvalue_1\tvalue_2\tln(fold_change)\ttest_stat\tp_value\tq_value\tsignificant\n");
 	for (size_t i = 1; i < tests.tss_group_de_tests.size(); ++i)
 	{
         for (size_t j = 0; j < i; ++j)
@@ -875,9 +1088,11 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
         }
 	}
 	
+    //fprintf(stderr, "***There are %lu difference records in gene_exp_diffs\n", gene_exp_diffs.size());
+    
 	int gene_exp_tests = fdr_significance(FDR, gene_exp_diffs);
 	fprintf(stderr, "Performed %d gene-level transcription difference tests\n", gene_exp_tests);
-	fprintf(outfiles.gene_de_outfile, "test_id\tgene\tlocus\tsample_1\tsample_2\tstatus\tvalue_1\tvalue_2\tln(fold_change)\ttest_stat\tp_value\tsignificant\n");
+	fprintf(outfiles.gene_de_outfile, "test_id\tgene_id\tgene\tlocus\tsample_1\tsample_2\tstatus\tvalue_1\tvalue_2\tln(fold_change)\ttest_stat\tp_value\tq_value\tsignificant\n");
     for (size_t i = 1; i < tests.gene_de_tests.size(); ++i)
 	{        
         for (size_t j = 0; j < i; ++j)
@@ -898,7 +1113,7 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
 	}
 	int cds_exp_tests = fdr_significance(FDR, cds_exp_diffs);
 	fprintf(stderr, "Performed %d CDS-level transcription difference tests\n", cds_exp_tests);
-	fprintf(outfiles.cds_de_outfile, "test_id\tgene\tlocus\tsample_1\tsample_2\tstatus\tvalue_1\tvalue_2\tln(fold_change)\ttest_stat\tp_value\tsignificant\n");
+	fprintf(outfiles.cds_de_outfile, "test_id\tgene_id\tgene\tlocus\tsample_1\tsample_2\tstatus\tvalue_1\tvalue_2\tln(fold_change)\ttest_stat\tp_value\tq_value\tsignificant\n");
     for (size_t i = 1; i < tests.cds_de_tests.size(); ++i)
 	{
         for (size_t j = 0; j < i; ++j)
@@ -920,7 +1135,7 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
 	
 	int splicing_tests = fdr_significance(FDR, splicing_diffs);
 	fprintf(stderr, "Performed %d splicing tests\n", splicing_tests);
-	fprintf(outfiles.diff_splicing_outfile, "test_id\tgene\tlocus\tsample_1\tsample_2\tstatus\tvalue_1\tvalue_2\tsqrt(JS)\ttest_stat\tp_value\tsignificant\n");
+	fprintf(outfiles.diff_splicing_outfile, "test_id\tgene_id\tgene\tlocus\tsample_1\tsample_2\tstatus\tvalue_1\tvalue_2\tsqrt(JS)\ttest_stat\tp_value\tq_value\tsignificant\n");
     for (size_t i = 1; i < tests.diff_splicing_tests.size(); ++i)
 	{
         for (size_t j = 0; j < i; ++j)
@@ -942,7 +1157,7 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
 	}
 	int promoter_tests = fdr_significance(FDR, promoter_diffs);
 	fprintf(stderr, "Performed %d promoter preference tests\n", promoter_tests);
-    fprintf(outfiles.diff_promoter_outfile, "test_id\tgene\tlocus\tsample_1\tsample_2\tstatus\tvalue_1\tvalue_2\tsqrt(JS)\ttest_stat\tp_value\tsignificant\n");
+    fprintf(outfiles.diff_promoter_outfile, "test_id\tgene_id\tgene\tlocus\tsample_1\tsample_2\tstatus\tvalue_1\tvalue_2\tsqrt(JS)\ttest_stat\tp_value\tq_value\tsignificant\n");
     for (size_t i = 1; i < tests.diff_promoter_tests.size(); ++i)
 	{
         for (size_t j = 0; j < i; ++j)
@@ -963,7 +1178,7 @@ void driver(FILE* ref_gtf, FILE* mask_gtf, vector<string>& sam_hit_filename_list
 	}
 	int cds_use_tests = fdr_significance(FDR, cds_use_diffs);
 	fprintf(stderr, "Performing %d relative CDS output tests\n", cds_use_tests);
-	fprintf(outfiles.diff_cds_outfile, "test_id\tgene\tlocus\tsample_1\tsample_2\tstatus\tvalue_1\tvalue_2\tsqrt(JS)\ttest_stat\tp_value\tsignificant\n");
+	fprintf(outfiles.diff_cds_outfile, "test_id\tgene_id\tgene\tlocus\tsample_1\tsample_2\tstatus\tvalue_1\tvalue_2\tsqrt(JS)\ttest_stat\tp_value\tq_value\tsignificant\n");
     for (size_t i = 1; i < tests.diff_cds_tests.size(); ++i)
 	{
         for (size_t j = 0; j < i; ++j)
@@ -993,16 +1208,27 @@ int main(int argc, char** argv)
 {
     init_library_table();
     
+    min_isoform_fraction = 1e-5;
+    
 	int parse_ret = parse_options(argc,argv);
     if (parse_ret)
         return parse_ret;
 	
+    if (!use_total_mass && !use_compat_mass)
+    {
+        use_total_mass = false;
+        use_compat_mass = true;   
+    }
+    
 	if(optind >= argc)
     {
         print_usage();
         return 1;
     }
-	
+    
+    if (!no_update_check)
+        check_version(PACKAGE_VERSION);
+    
     string ref_gtf_filename = argv[optind++];
 	
 	vector<string> sam_hit_filenames;
@@ -1034,9 +1260,12 @@ int main(int argc, char** argv)
         exit(1);
     }
     
+    if (random_seed == -1)
+        random_seed = time(NULL);
+    
 	// seed the random number generator - we'll need it for the importance
 	// sampling during MAP estimation of the gammas
-	srand48(time(NULL));
+	srand48(random_seed);
 	
 	FILE* ref_gtf = NULL;
 	if (ref_gtf_filename != "")
@@ -1066,7 +1295,7 @@ int main(int argc, char** argv)
 	
 	// Note: we don't want the assembly filters interfering with calculations 
 	// here
-	min_isoform_fraction = 0.0;
+	
 	pre_mrna_fraction = 0.0;
     olap_radius = 0;
 	
diff --git a/src/cufflinks.cpp b/src/cufflinks.cpp
index 374f179..16575ef 100644
--- a/src/cufflinks.cpp
+++ b/src/cufflinks.cpp
@@ -22,6 +22,7 @@
 
 #include <boost/thread.hpp>
 
+#include "update_check.h"
 #include "clustering.h"
 #include "abundances.h"
 #include "bundles.h"
@@ -29,51 +30,67 @@
 #include "genes.h"
 #include "assemble.h"
 #include "biascorrection.h"
-#include "gtf_tracking.h"
+#include "multireads.h"
 
 using namespace std;
 
 #if ENABLE_THREADS
-const char *short_options = "m:p:s:F:c:I:j:Q:L:G:f:o:M:r:a:A:Nqv";
+const char *short_options = "m:p:s:F:I:j:Q:L:G:g:o:M:b:a:A:Nqvu";
 #else
-const char *short_options = "m:s:F:c:I:j:Q:L:G:f:o:M:r:a:A:Nqv";
+const char *short_options = "m:s:F:I:j:Q:L:G:g:o:M:b:a:A:Nqvu";
 #endif
 
 static struct option long_options[] = {
-{"inner-dist-mean",			required_argument,       0,          'm'},
-{"inner-dist-stddev",		required_argument,       0,          's'},
-{"transcript-score-thresh", required_argument,       0,          't'},
+// general options
+{"GTF",					    required_argument,		 0,			 'G'},
+{"GTF-guide",			    required_argument,		 0,			 'g'},
+{"mask-gtf",                required_argument,		 0,			 'M'},
+{"library-type",		    required_argument,		 0,			 OPT_LIBRARY_TYPE},
+{"seed",                    required_argument,		 0,			 OPT_RANDOM_SEED},
+
+// program behavior
+{"output-dir",			    required_argument,		 0,			 'o'},
+{"verbose",			    	no_argument,		 	 0,			 'v'},
+{"quiet",			    	no_argument,			 0,			 'q'},
+{"no-update-check",         no_argument,             0,          OPT_NO_UPDATE_CHECK},
+#if ENABLE_THREADS
+    {"num-threads",			required_argument,       0,          'p'},
+#endif    
+{"output-fld",              no_argument,             0,          OPT_OUTPUT_FLD},
+{"output-bias-params",      no_argument,             0,          OPT_OUTPUT_BIAS_PARAMS},
+    
+// abundance estimation
+{"frag-len-mean",			required_argument,       0,          'm'},
+{"frag-len-std-dev",		required_argument,       0,          's'},
 {"min-isoform-fraction",    required_argument,       0,          'F'},
-{"min-intron-fraction",     required_argument,       0,          'f'},
+{"upper-quartile-normalization",  no_argument,	 		 0,	         'N'},
+{"frag-bias-correct",       required_argument,		 0,			 'b'},
+{"multi-read-correct",      no_argument,			 0,			 'u'},
+{"num-importance-samples",  required_argument,		 0,			 OPT_NUM_IMP_SAMPLES},
+{"max-mle-iterations",		required_argument,		 0,			 OPT_MLE_MAX_ITER},
+{"bias-mode",               required_argument,		 0,			 OPT_BIAS_MODE},
+{"use-grad-ascent",         no_argument,             0,			 OPT_USE_EM},
+{"no-collapse-cond-prob",   no_argument,             0,			 OPT_COLLAPSE_COND_PROB},
+{"compatible-hits-norm",    no_argument,	 		 0,	         OPT_USE_COMPAT_MASS},
+{"total-hits-norm",         no_argument,	 		 0,	         OPT_USE_TOTAL_MASS},
+    
+// assembly
 {"pre-mrna-fraction",		required_argument,		 0,			 'j'},
 {"junc-alpha",				required_argument,		 0,			 'a'},	
 {"small-anchor-fraction",	required_argument,		 0,			 'A'},
 {"max-intron-length",		required_argument,		 0,			 'I'},
-{"min-map-qual",			required_argument,		 0,			 'Q'},
 {"label",					required_argument,		 0,			 'L'},
-{"collapse-thresh",         required_argument,		 0,			 'c'},
-{"GTF",					    required_argument,		 0,			 'G'},
-{"mask-gtf",                required_argument,		 0,			 'M'},
-{"output-dir",			    required_argument,		 0,			 'o'},
-{"quartile-normalization",  no_argument,	 		 0,	         'N'},
-{"verbose",			    	no_argument,		 	0,			 'v'},
-{"quiet",			    	no_argument,		 	0,			 'q'},
-{"reference-seq",			required_argument,		 0,			 'r'},	
-#if ENABLE_THREADS
-{"num-threads",				required_argument,       0,          'p'},
-#endif
 {"overhang-tolerance",      required_argument,		 0,			 OPT_OVERHANG_TOLERANCE},
-
-{"num-importance-samples",  required_argument,		 0,			 OPT_NUM_IMP_SAMPLES},
-{"max-mle-iterations",		required_argument,		 0,			 OPT_MLE_MAX_ITER},
-{"library-type",		    required_argument,		 0,			 OPT_LIBRARY_TYPE},
+{"min-frags-per-transfrag",required_argument,		 0,			 OPT_MIN_FRAGS_PER_TRANSFRAG},
+{"min-intron-length",       required_argument,	     0,			 OPT_MIN_INTRON_LENGTH},
 {"max-bundle-length",       required_argument,		 0,			 OPT_MAX_BUNDLE_LENGTH},
-{"min-frags-per-transfrags",required_argument,		 0,			 OPT_MIN_FRAGS_PER_TRANSFRAG},
-{"min-intron-length",required_argument,		         0,			 OPT_MIN_INTRON_LENGTH},
+{"trim-3-dropoff-frac",     required_argument,		 0,			 OPT_3_PRIME_AVGCOV_THRESH},
+{"trim-3-avgcov-thresh",	required_argument,		 0,			 OPT_3_PRIME_AVGCOV_THRESH},
+    
+{"3-overhang-tolerance",	required_argument,		 0,			 OPT_3_OVERHANG_TOLERANCE},
+{"intron-overhang-tolerance",	required_argument,		 0,		 OPT_INTRON_OVERHANG_TOLERANCE},
+{"no-faux-reads",         no_argument,             0,           OPT_NO_FAUX_READS},
 
-#if ADAM_MODE
-{"bias-mode",		    required_argument,		 0,			 OPT_BIAS_MODE},
-#endif
 
 {0, 0, 0, 0} // terminator
 };
@@ -85,37 +102,52 @@ void print_usage()
     fprintf(stderr, "linked against Boost version %d\n", BOOST_VERSION);
     fprintf(stderr, "-----------------------------\n"); 
     fprintf(stderr, "Usage:   cufflinks [options] <hits.sam>\n");
-    fprintf(stderr, "Options:\n\n");
+    fprintf(stderr, "General Options:\n");
+    fprintf(stderr, "  -o/--output-dir              write all output files to this directory              [ default:     ./ ]\n");
 #if ENABLE_THREADS
     fprintf(stderr, "  -p/--num-threads             number of threads used during analysis                [ default:      1 ]\n");
-#endif
+#endif    
+    fprintf(stderr, "  -G/--GTF                     quantitate against reference transcript annotations                      \n");
+    fprintf(stderr, "  -g/--GTF-guide               use reference transcript annotation to guide assembly                   \n");
+    fprintf(stderr, "  -M/--mask-file               ignore all alignment within transcripts in this file                     \n");
+    fprintf(stderr, "  -b/--frag-bias-correct       use bias correction - reference fasta required        [ default:   NULL ]\n");
+    fprintf(stderr, "  -u/--multi-read-correct      use 'rescue method' for multi-reads (more accurate)   [ default:  FALSE ]\n");
+    fprintf(stderr, "  --library-type               library prep used for input reads                     [ default:  below ]\n");
     
+    fprintf(stderr, "\nAdvanced Abundance Estimation Options:\n");
+    fprintf(stderr, "  -m/--frag-len-mean           average fragment length (unpaired reads only)         [ default:    200 ]\n");
+    fprintf(stderr, "  -s/--frag-len-std-dev        fragment length std deviation (unpaired reads only)   [ default:     80 ]\n");
+    fprintf(stderr, "  --upper-quartile-norm        use upper-quartile normalization                      [ default:  FALSE ]\n");
+    fprintf(stderr, "  --max-mle-iterations         maximum iterations allowed for MLE calculation        [ default:   5000 ]\n");
+    fprintf(stderr, "  --num-importance-samples     number of importance samples for MAP restimation      [ default:   1000 ]\n");
+    fprintf(stderr, "  --compatible-hits-norm       count hits compatible with reference RNAs only        [ default:  FALSE ]\n");
+    fprintf(stderr, "  --total-hits-norm            count all hits for normalization                      [ default:  TRUE  ]\n");
+    
+    fprintf(stderr, "\nAdvanced Assembly Options:\n");
     fprintf(stderr, "  -L/--label                   assembled transcripts have this ID prefix             [ default:   CUFF ]\n");
-    fprintf(stderr, "  -G/--GTF                     quantitate against reference transcript annotations                      \n");
-    fprintf(stderr, "  -F/--min-isoform-fraction    suppress transcripts below this abundance level       [ default:   0.15 ]\n");
-    fprintf(stderr, "  -f/--min-intron-fraction     filter spliced alignments below this level            [ default:   0.05 ]\n");
+    fprintf(stderr, "  -F/--min-isoform-fraction    suppress transcripts below this abundance level       [ default:   0.10 ]\n");
     fprintf(stderr, "  -j/--pre-mrna-fraction       suppress intra-intronic transcripts below this level  [ default:   0.15 ]\n");
     fprintf(stderr, "  -I/--max-intron-length       ignore alignments with gaps longer than this          [ default: 300000 ]\n");
-    fprintf(stderr, "  -Q/--min-map-qual            ignore alignments with lower than this mapping qual   [ default:      0 ]\n");
-    fprintf(stderr, "  -M/--mask-file               ignore all alignment within transcripts in this file                     \n");
-    fprintf(stderr, "  -v/--verbose                 log-friendly verbose processing (no progress bar)     [ default:  FALSE ]\n");
-	fprintf(stderr, "  -q/--quiet                   log-friendly quiet processing (no progress bar)       [ default:  FALSE ]\n");
-    fprintf(stderr, "  -o/--output-dir              write all output files to this directory              [ default:     ./ ]\n");
-    fprintf(stderr, "  -r/--reference-seq           reference fasta file for sequence bias correction     [ default:   NULL ]\n");
-    fprintf(stderr, "\nAdvanced Options:\n\n");
-    fprintf(stderr, "  -N/--quartile-normalization  use quartile normalization instead of total counts    [ default:  FALSE ]\n");
-    fprintf(stderr, "  -a/--junc-alpha              alpha for junction binomial test filter               [ default:   0.01 ]\n");
-    fprintf(stderr, "  -A/--small-anchor-fraction   percent read overhang taken as 'suspiciously small'   [ default:   0.12 ]\n");
-    fprintf(stderr, "  -m/--frag-len-mean           the average fragment length                           [ default:    200 ]\n");
-    fprintf(stderr, "  -s/--frag-len-std-dev        the fragment length standard deviation                [ default:     80 ]\n");
+    fprintf(stderr, "  -a/--junc-alpha              alpha for junction binomial test filter               [ default:  0.001 ]\n");
+    fprintf(stderr, "  -A/--small-anchor-fraction   percent read overhang taken as 'suspiciously small'   [ default:   0.09 ]\n");
     fprintf(stderr, "  --min-frags-per-transfrag    minimum number of fragments needed for new transfrags [ default:     10 ]\n");
     fprintf(stderr, "  --overhang-tolerance         number of terminal exon bp to tolerate in introns     [ default:      8 ]\n");
-    fprintf(stderr, "  --num-importance-samples     number of importance samples for MAP restimation      [ default:   1000 ]\n");
-    fprintf(stderr, "  --max-mle-iterations         maximum iterations allowed for MLE calculation        [ default:   5000 ]\n");
-    fprintf(stderr, "  --library-type               Library prep used for input reads                     [ default:  below ]\n");
     fprintf(stderr, "  --max-bundle-length          maximum genomic length allowed for a given bundle     [ default:3500000 ]\n");
     fprintf(stderr, "  --min-intron-length          minimum intron size allowed in genome                 [ default:     50 ]\n");
+    fprintf(stderr, "  --trim-3-avgcov-thresh       minimum avg coverage required to attempt 3' trimming  [ default:     10 ]\n");
+    fprintf(stderr, "  --trim-3-dropoff-frac        fraction of avg coverage below which to trim 3' end   [ default:    0.1 ]\n");
     
+    fprintf(stderr, "\nAdvanced Reference Annotation Guided Assembly Options:\n");
+//    fprintf(stderr, "  --tile-read-len              length of faux-reads                                  [ default:    405 ]\n");
+//    fprintf(stderr, "  --tile-read-sep              distance between faux-reads                           [ default:     15 ]\n");
+    fprintf(stderr, "  --no-faux-reads              disable tiling by faux reads                          [ default:  FALSE ]\n");
+    fprintf(stderr, "  --3-overhang-tolerance       overhang allowed on 3' end when merging with reference[ default:    600 ]\n");
+    fprintf(stderr, "  --intron-overhang-tolerance  overhang allowed inside reference intron when merging [ default:     30 ]\n");
+    
+    fprintf(stderr, "\nAdvanced Program Behavior Options:\n");
+    fprintf(stderr, "  -v/--verbose                 log-friendly verbose processing (no progress bar)     [ default:  FALSE ]\n");
+    fprintf(stderr, "  -q/--quiet                   log-friendly quiet processing (no progress bar)       [ default:  FALSE ]\n");
+    fprintf(stderr, "  --no-update-check            do not contact server to check for update availability[ default:  FALSE ]\n");
     print_library_table();
 }
 
@@ -131,14 +163,13 @@ int parse_options(int argc, char** argv)
 			case -1:     /* Done with options. */
 				break;
 			case 'm':
+				user_provided_fld = true;
 				def_frag_len_mean = (uint32_t)parseInt(0, "-m/--frag-len-mean arg must be at least 0", print_usage);
 				break;
 			case 's':
+				user_provided_fld = true;
 				def_frag_len_std_dev = (uint32_t)parseInt(0, "-s/--frag-len-std-dev arg must be at least 0", print_usage);
 				break;
-			case 't':
-				transcript_score_thresh = parseFloat(-99999999, 0, "-t/--transcript-score-thresh must less than or equal to 0", print_usage);
-				break;
 			case 'p':
 				num_threads = (uint32_t)parseInt(1, "-p/--num-threads arg must be at least 1", print_usage);
 				break;
@@ -146,9 +177,6 @@ int parse_options(int argc, char** argv)
 				min_isoform_fraction = parseFloat(0, 1.0, "-F/--min-isoform-fraction must be between 0 and 1.0", print_usage);
 				F_set = true;
 				break;
-			case 'f':
-				min_isoform_fraction = parseFloat(0, 1.0, "-f/--min-intron-fraction must be between 0 and 1.0", print_usage);
-				break;
 			case 'I':
 				max_intron_length = parseInt(1, "-I/--max-intron-length must be at least 1", print_usage);
 				break;
@@ -172,22 +200,22 @@ int parse_options(int argc, char** argv)
 				max_mle_iterations = parseInt(1, "--max-mle-iterations must be at least 1", print_usage);
 				break;
 			case OPT_BIAS_MODE:
-				bias_mode = optarg;
-				break;
-			case 'Q':
-			{
-				int min_map_qual = parseInt(0, "-Q/--min-map-qual must be at least 0", print_usage);
-				if (min_map_qual > 0)
-				{
-					long double p = (-1.0 * min_map_qual) / 10.0;
-					max_phred_err_prob = pow(10.0L, p);
-				}
+				if (!strcmp(optarg, "site"))
+					bias_mode = SITE;
+				else if (!strcmp(optarg, "pos"))
+					bias_mode = POS;
+				else if (!strcmp(optarg, "pos_vlmm"))
+					bias_mode = POS_VLMM;
+				else if (!strcmp(optarg, "vlmm"))
+					bias_mode = VLMM;
+                else if (!strcmp(optarg, "pos_site"))
+					bias_mode = POS_SITE;
 				else
 				{
-					max_phred_err_prob = 1.0;
+					fprintf(stderr, "Unknown bias mode.\n");
+					exit(1);
 				}
 				break;
-			}
 			case 'L':
 			{
 				user_label = optarg;
@@ -196,7 +224,15 @@ int parse_options(int argc, char** argv)
 			case 'G':
 			{
 				ref_gtf_filename = optarg;
-				ref_driven = true;
+				bundle_mode = REF_DRIVEN;
+                init_bundle_mode = REF_DRIVEN;
+				break;
+			}
+			case 'g':
+			{
+				ref_gtf_filename = optarg;
+				bundle_mode = REF_GUIDED;
+                init_bundle_mode = REF_GUIDED;
 				break;
 			}
             case 'M':
@@ -235,12 +271,17 @@ int parse_options(int argc, char** argv)
 				output_dir = optarg;
 				break;
 			}
-            case 'r':
+            case 'b':
 			{
 				fasta_dir = optarg;
 				corr_bias = true;
 				break;
             }    
+			case 'u':
+			{
+				corr_multi = true;
+				break;
+			}
             case OPT_LIBRARY_TYPE:
 			{
 				library_type = optarg;
@@ -251,7 +292,6 @@ int parse_options(int argc, char** argv)
 				max_gene_length = parseInt(1, "--max-bundle-length must be at least 1", print_usage);;
 				break;
 			}
-            
             case OPT_MIN_FRAGS_PER_TRANSFRAG:
 			{
 				min_frags_per_transfrag = parseInt(0, "--min-frags-per-transfrag must be at least 0", print_usage);;
@@ -262,7 +302,71 @@ int parse_options(int argc, char** argv)
 				min_intron_length = parseInt(0, "--min-intron-length must be at least 0", print_usage);
 				break;
 			}
-                
+			case OPT_3_PRIME_AVGCOV_THRESH:
+			{
+				trim_3_avgcov_thresh = parseFloat(0, 9999999, "--trim-3-avgcov-thresh must be at least 0", print_usage);
+				break;
+			}
+            case OPT_3_PRIME_DROPOFF_FRAC:
+			{
+				trim_3_dropoff_frac = parseFloat(0, 1.0, "--trim-3-dropoff-frac must be between 0 and 1.0", print_usage);
+				break;
+			}
+            case OPT_NO_UPDATE_CHECK:
+            {
+                no_update_check = true;
+                break;
+            }
+            case OPT_OUTPUT_FLD:
+            {
+                output_fld = true;
+                break;
+            }
+            case OPT_OUTPUT_BIAS_PARAMS:
+            {
+                output_bias_params = true;
+                break;
+            }
+            case OPT_USE_EM:
+            {
+                use_em = false;
+                break;
+            }
+            case OPT_COLLAPSE_COND_PROB:
+            {
+                cond_prob_collapse = false;
+                break;
+            }
+            case OPT_NO_FAUX_READS:
+            {
+                enable_faux_reads = false;
+                break;
+            }
+            case OPT_3_OVERHANG_TOLERANCE:
+            {
+                overhang_3 = parseInt(0, "--3-overhang-tolernace must be at least 0", print_usage);
+                break;
+            }
+            case OPT_INTRON_OVERHANG_TOLERANCE:
+            {
+                ref_merge_overhang_tolerance = parseInt(0, "--intron-overhang-tolernace must be at least 0", print_usage);
+                break;
+            }
+            case OPT_RANDOM_SEED:
+            {
+                random_seed = parseInt(0, "--seed must be at least 0", print_usage);
+                break;
+            }
+            case OPT_USE_COMPAT_MASS:
+            {
+                use_compat_mass = true;
+                break;
+            }
+            case OPT_USE_TOTAL_MASS:
+            {
+                use_total_mass = true;
+                break;
+            }
 			default:
 				print_usage();
 				return 1;
@@ -270,13 +374,17 @@ int parse_options(int argc, char** argv)
     } while(next_option != -1);
 	
     
-	if (ref_gtf_filename != "")
+	if (bundle_mode == REF_DRIVEN)
 	{
         if (!F_set)
         {
             min_isoform_fraction = 0.0;
         }
-		allow_junk_filtering = false;	
+	}
+	
+	if (bundle_mode == REF_DRIVEN)
+	{
+		allow_junk_filtering = false;
 	}
 	
     if (library_type != "")
@@ -290,53 +398,146 @@ int parse_options(int argc, char** argv)
         }
         else 
         {
-            if (library_type == "transfrags")
-            {
-                allow_junk_filtering = false;
-            }
+//            if (library_type == "transfrags")
+//            {
+//                allow_junk_filtering = false;
+//            }
             global_read_properties = &lib_itr->second;
         }
     }
-	
-#if ADAM_MODE
-	if (corr_bias)
-		output_dir = output_dir + "/" + bias_mode;
-#endif
-	
     
-    //inner_dist_norm = normal(0, inner_dist_std_dev);
-	return 0;
+    if (use_total_mass && use_compat_mass)
+    {
+        fprintf (stderr, "Error: please supply only one of --compatibile-hits-norm and --total-hits-norm\n");
+        exit(1);
+    }
+    if (use_compat_mass && bundle_mode != REF_DRIVEN)
+    {
+        fprintf (stderr, "Error: cannot use --compatible-hits-norm without --GTF\n");
+        exit(1);
+    }
+	
+    return 0;
 }
 
-void add_non_shadow_scaffs(const vector<Scaffold>& lhs, 
-						   const vector<Scaffold>& rhs,
+void combine_strand_assemblies(vector<Scaffold>& lhs, 
+						   vector<Scaffold>& rhs,
 						   vector<Scaffold>& scaffolds,
-						   bool include_unknown_strand)
+						   vector<shared_ptr<Scaffold> >* ref_scaffs)
 {
-	for (size_t i = 0; i < lhs.size(); ++i)
+	// first check for strand support
+    for (size_t l = 0; l < lhs.size(); ++l)
+    {
+		if (!lhs[l].has_strand_support(ref_scaffs))
+			lhs[l].strand(CUFF_STRAND_UNKNOWN);
+	}
+	for (size_t r = 0; r < rhs.size(); ++r)
+    {
+		if (!rhs[r].has_strand_support(ref_scaffs))
+			rhs[r].strand(CUFF_STRAND_UNKNOWN);
+	}
+	
+    vector<bool> kept_lhs(lhs.size(), true);
+    vector<bool> kept_rhs(rhs.size(), true);
+    
+	// next filter both lists based on reference transcripts (if available)
+	if (ref_scaffs != NULL)
 	{
-		bool add_to_asm = true;
-		if (lhs[i].strand() == CUFF_STRAND_UNKNOWN)
+		for(size_t l = 0; l < lhs.size(); ++l)
+		{			
+			foreach(shared_ptr<Scaffold> ref_scaff, *ref_scaffs)
+			{
+				if (ref_scaff->left() >= lhs[l].right() + overhang_3)
+				{
+					break;
+				}
+				else if (ref_scaff->contains(lhs[l], 0, overhang_3) && Scaffold::compatible(*ref_scaff, lhs[l], ref_merge_overhang_tolerance))
+				{
+					kept_lhs[l] = false;
+				}
+				else if (ref_scaff->overlapped_3(lhs[l], 0, overhang_3) && Scaffold::compatible(*ref_scaff, lhs[l], ref_merge_overhang_tolerance))
+				{
+					ref_scaff->extend_5(lhs[l]);
+					kept_lhs[l] = false;
+				}
+			}
+		}
+		for(size_t r = 0; r < rhs.size(); ++r)
+		{			
+			foreach(shared_ptr<Scaffold> ref_scaff, *ref_scaffs)
+			{
+				if (ref_scaff->left() >= rhs[r].right() + overhang_3)
+				{
+					break;
+				}
+				else if (ref_scaff->contains(rhs[r], 0, overhang_3) && Scaffold::compatible(*ref_scaff, rhs[r], ref_merge_overhang_tolerance))
+				{
+					kept_rhs[r] = false;
+				}
+				else if (ref_scaff->overlapped_3(rhs[r], 0, overhang_3) && Scaffold::compatible(*ref_scaff, rhs[r], ref_merge_overhang_tolerance))
+				{
+					ref_scaff->extend_5(rhs[r]);
+					kept_rhs[r] = false;
+				}
+			}
+		}
+	}
+	
+    // We want to keep all fwd, all reverse, and only the non-redundant unknowns
+    // if two unknown strand frags olap, merge them.
+    for (size_t l = 0; l < lhs.size(); ++l)
+    {		 
+		if (!kept_lhs[l])
+			continue;
+		bool lhs_support = (lhs[l].strand() != CUFF_STRAND_UNKNOWN);
+        
+		for (size_t r = 0; r < rhs.size(); ++r)
 		{
-			for (size_t j = 0; j < rhs.size(); ++j)
+			if (!kept_rhs[r])
+				continue;
+			if (Scaffold::overlap_in_genome(lhs[l], rhs[r], 0))
 			{
-				if (include_unknown_strand || 
-					rhs[j].strand() != CUFF_STRAND_UNKNOWN)
+				if (Scaffold::compatible(lhs[l], rhs[r]))
 				{
-					if (Scaffold::overlap_in_genome(lhs[i], rhs[j], 0) &&
-						Scaffold::compatible(lhs[i], rhs[j]))
+					bool rhs_support = (rhs[r].strand() != CUFF_STRAND_UNKNOWN);
+					if (!lhs_support && !rhs_support)
 					{
-						add_to_asm = false;
+						Scaffold merged;
+						Scaffold::merge(lhs[l],rhs[r],merged, true);
+						scaffolds.push_back(merged);
+						kept_lhs[l] = false;
+						kept_rhs[r] = false;
 						break;
 					}
+					else if (lhs_support && !rhs_support)
+					{
+						kept_rhs[r] = false;
+					}
+					else if (!lhs_support && rhs_support)
+					{
+						kept_lhs[l] = false;
+						break;
+					}					
 				}
 			}
-		}
-		if (add_to_asm)
-		{
-			scaffolds.push_back(lhs[i]);	
-		}
-	}
+		}		
+    }
+    
+    // first trim off any polymerase run-ons, and make 3' ends consistent
+    clip_by_3_prime_dropoff(lhs);
+    clip_by_3_prime_dropoff(rhs);
+
+    for (size_t i = 0; i < lhs.size(); ++i)
+    {
+        if (kept_lhs[i])
+            scaffolds.push_back(lhs[i]);
+    }
+    
+    for (size_t i = 0; i < rhs.size(); ++i)
+    {
+        if (kept_rhs[i])
+            scaffolds.push_back(rhs[i]);
+    }
 }
 
 void guess_strand(int bundle_origin, 
@@ -370,19 +571,17 @@ CuffStrand guess_strand_for_interval(const vector<uint8_t>& strand_guess,
 	return (CuffStrand)guess;
 }
 
+
 bool scaffolds_for_bundle(const HitBundle& bundle, 
 						  vector<shared_ptr<Scaffold> >& scaffolds,
+						  vector<shared_ptr<Scaffold> >* ref_scaffs = NULL,
 						  BundleStats* stats = NULL)
 {
+	bool ref_guided = (ref_scaffs != NULL);
+	
 	vector<Scaffold> hits;
 	vector<Scaffold> tmp_scaffs;
 	
-    //	for (size_t i = 0; i < bundle.non_redundant_hits().size(); ++i)
-    //	{
-    //		const MateHit& hit = bundle.non_redundant_hits()[i];
-    //		hits.push_back(Scaffold(hit));
-    //	}
-    
 	for (size_t i = 0; i < bundle.hits().size(); ++i)
 	{
 		const MateHit& hit = bundle.hits()[i];
@@ -410,6 +609,19 @@ bool scaffolds_for_bundle(const HitBundle& bundle,
                        true);
     }
     
+	if (ref_guided && enable_faux_reads && !hits.empty())
+	{
+		vector<Scaffold> pseudohits;
+		foreach(shared_ptr<Scaffold const> ref_scaff, *ref_scaffs)
+		{
+			ref_scaff->tile_with_scaffs(pseudohits, tile_len, tile_off);
+		}
+		hits.insert(hits.end(),
+					pseudohits.begin(),
+					pseudohits.end());
+		inplace_merge(hits.begin(),hits.end()-pseudohits.size(), hits.end(), scaff_lt);
+	}
+	
 	vector<uint8_t> strand_guess(bundle.length(), CUFF_STRAND_UNKNOWN);
 	guess_strand(bundle.left(),
 				 hits,
@@ -419,6 +631,7 @@ bool scaffolds_for_bundle(const HitBundle& bundle,
 	{
 		if (hits[i].strand() == CUFF_STRAND_UNKNOWN)
 		{
+            assert (!hits[i].has_intron());
 			uint8_t guess = CUFF_STRAND_UNKNOWN;
 			Scaffold& hit = hits[i];
 			const vector<AugmentedCuffOp>& ops = hit.augmented_ops();
@@ -444,7 +657,6 @@ bool scaffolds_for_bundle(const HitBundle& bundle,
 		}
 	}
 	
-	vector<Scaffold> fwd_hits, rev_hits;
 	bool saw_fwd = false;
 	bool saw_rev = false;
 	
@@ -452,23 +664,18 @@ bool scaffolds_for_bundle(const HitBundle& bundle,
 	{
 		const Scaffold& hit = hits[i];
 		CuffStrand hs = hit.strand();
+						
 		if (hs == CUFF_FWD)
 			saw_fwd = true;
 		if (hs == CUFF_REV)
 			saw_rev = true;
 		
-		if (hs != CUFF_REV) 
-			fwd_hits.push_back(hit);
-		if (hs != CUFF_FWD)
-			rev_hits.push_back(hit);
-	}
-	
-	{
-		asm_verbose ("%s\tFiltering forward strand\n", bundle_label->c_str());
-		filter_hits(bundle.length(), bundle.left(), fwd_hits);
-		asm_verbose ("%s\tFiltering reverse strand\n", bundle_label->c_str());
-		filter_hits(bundle.length(), bundle.left(), rev_hits);
+//		if (hs != CUFF_REV) 
+//			fwd_hits.push_back(hit);
+//		if (hs != CUFF_FWD)
+//			rev_hits.push_back(hit);
 	}
+
     
 	vector<Scaffold> fwd_scaffolds;
 	vector<Scaffold> rev_scaffolds;
@@ -477,39 +684,94 @@ bool scaffolds_for_bundle(const HitBundle& bundle,
 	
 	if (saw_fwd && saw_rev)
 	{
-		assembled_successfully |= make_scaffolds(bundle.left(), 
-                                                 bundle.length(), 
-                                                 fwd_hits, 
-                                                 fwd_scaffolds);
+        // Forward strand hits
+        {
+            vector<Scaffold> fwd_hits;
+            for (size_t i = 0; i < hits.size(); ++i)
+            {
+                const Scaffold& hit = hits[i];
+                CuffStrand hs = hit.strand();
+                if (hs != CUFF_REV) 
+                    fwd_hits.push_back(hit);
+            }
+            
+            verbose_msg ("%s\tFiltering forward strand\n", bundle_label->c_str());
+            filter_hits(bundle.length(), bundle.left(), fwd_hits);
+            assembled_successfully |= make_scaffolds(bundle.left(), 
+                                                     bundle.length(), 
+                                                     fwd_hits, 
+                                                     fwd_scaffolds);
+        }
         
-		assembled_successfully |= make_scaffolds(bundle.left(), 
-                                                 bundle.length(), 
-                                                 rev_hits, 
-                                                 rev_scaffolds);
-		
-		add_non_shadow_scaffs(fwd_scaffolds, rev_scaffolds, tmp_scaffs, true);
-		add_non_shadow_scaffs(rev_scaffolds, fwd_scaffolds, tmp_scaffs, false);
+        // Reverse strand hits
+        {
+            vector<Scaffold> rev_hits;
+            for (size_t i = 0; i < hits.size(); ++i)
+            {
+                const Scaffold& hit = hits[i];
+                CuffStrand hs = hit.strand();
+                if (hs != CUFF_FWD)
+                    rev_hits.push_back(hit);
+            }
+            
+            verbose_msg ("%s\tFiltering reverse strand\n", bundle_label->c_str());
+            filter_hits(bundle.length(), bundle.left(), rev_hits);
+            assembled_successfully |= make_scaffolds(bundle.left(), 
+                                                     bundle.length(), 
+                                                     rev_hits, 
+                                                     rev_scaffolds);
+        }
 	}
 	else
 	{
 		if (saw_fwd || (!saw_fwd && !saw_rev))
 		{
-			assembled_successfully |= make_scaffolds(bundle.left(),
-													 bundle.length(),
-													 fwd_hits,
-													 fwd_scaffolds);
-			tmp_scaffs.insert(tmp_scaffs.end(),fwd_scaffolds.begin(), fwd_scaffolds.end());
+            // Forward strand hits
+            {
+                vector<Scaffold> fwd_hits;
+                for (size_t i = 0; i < hits.size(); ++i)
+                {
+                    const Scaffold& hit = hits[i];
+                    CuffStrand hs = hit.strand();
+                    if (hs != CUFF_REV) 
+                        fwd_hits.push_back(hit);
+                }
+                
+                verbose_msg ("%s\tFiltering forward strand\n", bundle_label->c_str());
+                filter_hits(bundle.length(), bundle.left(), fwd_hits);
+                assembled_successfully |= make_scaffolds(bundle.left(), 
+                                                         bundle.length(), 
+                                                         fwd_hits, 
+                                                         fwd_scaffolds);
+            
+            }
 		}
 		else
 		{
-			assembled_successfully |= make_scaffolds(bundle.left(), 
-													 bundle.length(), 
-													 rev_hits, 
-													 rev_scaffolds);
-			tmp_scaffs.insert(tmp_scaffs.end(),rev_scaffolds.begin(), rev_scaffolds.end());
+            // Reverse strand hits
+            {
+                vector<Scaffold> rev_hits;
+                for (size_t i = 0; i < hits.size(); ++i)
+                {
+                    const Scaffold& hit = hits[i];
+                    CuffStrand hs = hit.strand();
+                    if (hs != CUFF_FWD)
+                        rev_hits.push_back(hit);
+                }
+                
+                verbose_msg ("%s\tFiltering reverse strand\n", bundle_label->c_str());
+                filter_hits(bundle.length(), bundle.left(), rev_hits);
+                assembled_successfully |= make_scaffolds(bundle.left(), 
+                                                         bundle.length(), 
+                                                         rev_hits, 
+                                                         rev_scaffolds);
+            }
 		}
 	}
 	
+	combine_strand_assemblies(fwd_scaffolds, rev_scaffolds, tmp_scaffs, ref_scaffs);
+
+	
 	// Make sure all the reads are accounted for, including the redundant ones...
 	for (size_t i = 0; i < tmp_scaffs.size(); ++i)
 	{
@@ -521,22 +783,27 @@ bool scaffolds_for_bundle(const HitBundle& bundle,
 		}
 	}
 	
-	sort(tmp_scaffs.begin(), tmp_scaffs.end(), scaff_lt);
-	
-	foreach(Scaffold& scaff, tmp_scaffs)
+	if (ref_guided)
 	{
-		scaffolds.push_back(shared_ptr<Scaffold>(new Scaffold(scaff)));
+		scaffolds = *ref_scaffs;
 	}
+	if (assembled_successfully)
+	{
+		foreach(Scaffold& scaff, tmp_scaffs)
+		{
+			scaffolds.push_back(shared_ptr<Scaffold>(new Scaffold(scaff)));
+		}
+	}
+	sort(scaffolds.begin(), scaffolds.end(), scaff_lt_sp);
 	
 	return assembled_successfully;
 }
 
-
 //static long double min_abundance = 0.000001;
 
 #if ENABLE_THREADS
-mutex out_file_lock;
-mutex thread_pool_lock;
+boost::mutex out_file_lock;
+boost::mutex thread_pool_lock;
 int curr_threads = 0;
 
 void decr_pool_count()
@@ -581,28 +848,94 @@ void quantitate_transcript_cluster(AbundanceGroup& transfrag_cluster,
     {
         transfrag_cluster.calculate_abundance(hits_in_cluster);
 	}
+    else
+    {
+        vector<shared_ptr<Abundance> >& abundances = transfrag_cluster.abundances();
+        
+        int N = abundances.size();
+        double total_fpkm = 0.0;
+        vector<double> gammas;
+        for (size_t j = 0; j < N; ++j)
+        {
+            double FPKM = abundances[j]->transfrag()->fpkm();
+            abundances[j]->FPKM(FPKM);
+            total_fpkm += FPKM;
+            gammas.push_back(FPKM);
+        }
+        
+        for (size_t j = 0; j < N; ++j)
+        {
+            if (total_fpkm)
+                gammas[j] /= total_fpkm;
+        }
+        
+        vector<shared_ptr<Abundance> > filtered_transcripts = abundances;
+        filter_junk_isoforms(filtered_transcripts, gammas, abundances, 0);
+        vector<bool> to_keep (abundances.size(), false);
+        for(size_t i = 0; i < abundances.size(); ++i)
+        {
+            shared_ptr<Abundance> ab_i = abundances[i];
+            bool found = false;
+            foreach (shared_ptr<Abundance> ab_j, filtered_transcripts)
+            {
+                if (ab_i == ab_j)
+                {
+                    found = true;
+                    break;
+                }
+            }
+            if (found)
+                to_keep[i] = true;
+        }
+        
+        AbundanceGroup kept;
+        transfrag_cluster.filter_group(to_keep, kept);
+        transfrag_cluster = kept;
+    }
     
 	vector<AbundanceGroup> transfrags_by_strand;
 	cluster_transcripts<ConnectByStrand>(transfrag_cluster,
 										 transfrags_by_strand);
 	
+	
 	foreach (const AbundanceGroup& strand_group, transfrags_by_strand)
 	{	
 		vector<AbundanceGroup> transfrags_by_gene;
 		
-		cluster_transcripts<ConnectByAnnotatedGeneId>(strand_group,
-													  transfrags_by_gene);
-		
-		foreach (const AbundanceGroup& gene, transfrags_by_gene)
+		if (bundle_mode == REF_DRIVEN)
+		{
+			cluster_transcripts<ConnectByAnnotatedGeneId>(strand_group, transfrags_by_gene);
+		}
+		else
+		{
+			cluster_transcripts<ConnectByExonOverlap>(strand_group, transfrags_by_gene);
+		}
+
+		foreach(const AbundanceGroup& gene, transfrags_by_gene)
 		{
 			const vector<shared_ptr<Abundance> >& iso_abundances = gene.abundances();
 			vector<Isoform> isoforms;
 			
-            int gene_id = get_next_gene_id();
-            
+			int gene_id = -1;
+			int num_ref_gene_ids = 0;
+            bool has_novel_isoform = false;
+			string ref_gene_id = "";
+			
 			double major_isoform_FPKM = 0;
 			foreach (shared_ptr<Abundance> iso_ab, iso_abundances)
 			{
+				if (iso_ab->transfrag()->is_ref())
+				{
+					if (iso_ab->transfrag()->annotated_gene_id() != ref_gene_id)
+					{
+						ref_gene_id = iso_ab->transfrag()->annotated_gene_id();
+						num_ref_gene_ids++;
+					}
+				}
+                else
+                {
+                    has_novel_isoform = true;
+                }
 				major_isoform_FPKM = max(iso_ab->FPKM(), major_isoform_FPKM);
 			}
 			
@@ -620,12 +953,16 @@ void quantitate_transcript_cluster(AbundanceGroup& transfrag_cluster,
 				
 				density_per_bp *= (total_map_mass / 1000000.0); // yields (mass/(length/1000))
 				density_per_bp *= (s_len/ 1000.0);
+                double estimated_count = density_per_bp;
 				density_per_bp /= s_len;
 				density_per_bp *= avg_read_length;
 				//double density_per_bp = (FPKM * (map_mass / 1000000.0) * 1000.0);
 				
-				if (!allow_junk_filtering || density_score > min_isoform_fraction || major_isoform_FPKM == 0.0)
+				if (!allow_junk_filtering || transfrag->is_ref() || density_score > min_isoform_fraction)
 				{
+					if (gene_id == -1 && (has_novel_isoform || num_ref_gene_ids > 1))
+						gene_id = get_next_gene_id();
+					
 					isoforms.push_back(Isoform(*transfrag,
 											   gene_id,
 											   (int)isoforms.size() + 1,
@@ -634,8 +971,10 @@ void quantitate_transcript_cluster(AbundanceGroup& transfrag_cluster,
 											   iso_ab->gamma(),
 											   iso_ab->FPKM_conf(),
 											   density_per_bp, 
+                                               estimated_count,
 											   density_score,
-											   iso_ab->status()));
+											   iso_ab->status(),
+											   ref_gene_id));
 				}
 			}
 			
@@ -645,6 +984,7 @@ void quantitate_transcript_cluster(AbundanceGroup& transfrag_cluster,
 				genes.push_back(g);	
 			}
 		}
+		
 	}
     
 }
@@ -691,7 +1031,7 @@ void quantitate_transcript_clusters(vector<shared_ptr<Scaffold> >& scaffolds,
 	{
 		quantitate_transcript_cluster(cluster, total_map_mass, genes);
 	}
-    asm_verbose( "%s\tBundle quantitation complete\n", bundle_label->c_str());
+    verbose_msg( "%s\tBundle quantitation complete\n", bundle_label->c_str());
 }
 
 void assemble_bundle(const RefSequenceTable& rt,
@@ -727,25 +1067,32 @@ void assemble_bundle(const RefSequenceTable& rt,
 	
 	vector<shared_ptr<Scaffold> > scaffolds;
 	
-	if (ref_driven)
+	switch(bundle_mode)
 	{
-		scaffolds = bundle.ref_scaffolds();
-		if (!final_est_run && scaffolds.size() != 1) // Only learn bias on single isoforms
-		{
-			delete bundle_ptr;
-			return;
-		}
+		case REF_DRIVEN:
+			scaffolds = bundle.ref_scaffolds();
+			if (!final_est_run && scaffolds.size() != 1) // Only learn bias on single isoforms
+			{
+				delete bundle_ptr;
+				return;
+			}
+			break;
+		case REF_GUIDED:
+			scaffolds_for_bundle(bundle, scaffolds, &bundle.ref_scaffolds());
+			break;
+		case HIT_DRIVEN:
+			scaffolds_for_bundle(bundle, scaffolds);
+			break;
+		default:
+			assert(false);
 	}
-	else 
+	
+	if (scaffolds.empty())
 	{
-		bool success = scaffolds_for_bundle(bundle, scaffolds, NULL);
-		if (!success)
-		{
-			delete bundle_ptr;
-			return;
-		}
+		delete bundle_ptr;
+		return;
 	}
-	
+		
 	vector<Gene> genes;
     
     // FIXME: this routine does more than just quantitation, and should be 
@@ -754,13 +1101,13 @@ void assemble_bundle(const RefSequenceTable& rt,
                                    map_mass,
                                    genes);
     
-    asm_verbose( "%s\tFiltering bundle assembly\n", bundle_label->c_str());
+    verbose_msg( "%s\tFiltering bundle assembly\n", bundle_label->c_str());
     
     if (allow_junk_filtering)
         filter_junk_genes(genes);
 
 	
-	if (!final_est_run && ref_driven) // Bias needs to be learned
+	if (!final_est_run && bundle_mode==REF_DRIVEN) // Bias needs to be learned
 	{
 		for (size_t i = 0; i < genes.size(); ++i)
 		{
@@ -774,12 +1121,33 @@ void assemble_bundle(const RefSequenceTable& rt,
 #if ENABLE_THREADS	
 	out_file_lock.lock();
 #endif
+    
+    // Get hit_introns for full_read_support test if ref-guided
+    set<AugmentedCuffOp>* hit_introns = NULL;
+    if (init_bundle_mode == REF_GUIDED)
+    {
+        hit_introns = new set<AugmentedCuffOp>();
+        foreach(const MateHit& h, bundle.non_redundant_hits())
+        {
+            Scaffold s(h);
+            foreach (AugmentedCuffOp a, s.augmented_ops())
+            {
+                if (a.opcode == CUFF_INTRON)
+                {
+                    hit_introns->insert(a);
+                }
+            }
+        }
+    }
+    
 	
 	size_t num_scaffs_reported = 0;
 	for (size_t i = 0; i < genes.size(); ++i)
 	{
 		const Gene& gene = genes[i];
 		const vector<Isoform>& isoforms = gene.isoforms();
+        set<string> annotated_gene_names;
+        set<string> annotated_tss_ids;
 		for (size_t j = 0; j < isoforms.size(); ++j)
 		{
 			const Isoform& iso = isoforms[j];
@@ -791,7 +1159,7 @@ void assemble_bundle(const RefSequenceTable& rt,
 			
 			vector<string> isoform_exon_recs;
             
-			iso.get_gtf(isoform_exon_recs, rt);
+			iso.get_gtf(isoform_exon_recs, rt, hit_introns);
 			
 			for (size_t g = 0; g < isoform_exon_recs.size(); ++g)
 			{
@@ -803,57 +1171,80 @@ void assemble_bundle(const RefSequenceTable& rt,
 			const char* status;
 			if (iso.status()==NUMERIC_OK) 
 				status = "OK";
-			else 
+			else if (iso.status() == NUMERIC_LOW_DATA)
+                status = "LOWDATA";
+            else if (iso.status() == NUMERIC_FAIL)
 				status = "FAIL";
+            else
+                assert (false);
 			
-			fprintf(ftrans_abundances,"%s\t%d\t%s\t%d\t%d\t%lg\t%lg\t%lg\t%lg\t%lg\t%lg\t%d\t%lg\t%s\n", 
+			fprintf(ftrans_abundances,"%s\t%c\t%s\t%s\t%s\t%s\t%s:%d-%d\t%d\t%lg\t%s\t%lg\t%lg\t%lg\n", 
 					iso.trans_id().c_str(),
-					bundle.id(),
+                    (iso.scaffold().nearest_ref_classcode() == 0 ? '-' : iso.scaffold().nearest_ref_classcode()),
+                    (iso.scaffold().nearest_ref_id() == "" ? "-" : iso.scaffold().nearest_ref_id().c_str()),
+                    gene.gene_id().c_str(),
+                    (iso.scaffold().annotated_gene_name() == "" ? "-" : iso.scaffold().annotated_gene_name().c_str()), 
+                    (iso.scaffold().annotated_tss_id() == "" ? "-" : iso.scaffold().annotated_tss_id().c_str()),
 					rt.get_name(bundle.ref_id()),
 					iso.scaffold().left(),
 					iso.scaffold().right(),
-					iso.FPKM(),
-					iso.FMI(),
-					iso.fraction(),
+                    iso.scaffold().length(),
+                    iso.coverage(),
+                    status,
+                    iso.FPKM(),
 					iso.confidence().low,
-					iso.confidence().high,
-					iso.coverage(),
-					iso.scaffold().length(),
-					iso.effective_length(),
-					status);
+					iso.confidence().high);
 			fflush(ftrans_abundances);
 			
+            annotated_gene_names.insert(iso.scaffold().annotated_gene_name());
+            annotated_tss_ids.insert(iso.scaffold().annotated_tss_id());
+            
 			num_scaffs_reported++;
 		}
 		
 		const char* status;
-		if (gene.status()==NUMERIC_OK)
-			status = "OK";
-		else
-			status = "FAIL";
+        if (gene.status()==NUMERIC_OK) 
+            status = "OK";
+        else if (gene.status() == NUMERIC_LOW_DATA)
+            status = "LOWDATA";
+        else if (gene.status() == NUMERIC_FAIL)
+            status = "FAIL";
+        else
+            assert (false);
 
-		fprintf(fgene_abundances,"%s\t%d\t%s\t%d\t%d\t%lg\t%lg\t%lg\t%s\n",
-				gene.gene_id().c_str(),
-				bundle.id(),
-				rt.get_name(bundle.ref_id()),
-				gene.left(),
-				gene.right(),
-				gene.FPKM(),
-				gene.confidence().low,
-				gene.confidence().high,
-				status);
+        string gene_names = cat_strings(annotated_gene_names);
+        if (gene_names == "") gene_names = "-";
+        string tss_ids = cat_strings(annotated_tss_ids);
+        if (tss_ids == "") tss_ids = "-";
+        
+        fprintf(fgene_abundances,"%s\t%c\t%s\t%s\t%s\t%s\t%s:%d-%d\t%s\t%s\t%s\t%lg\t%lg\t%lg\n",
+                gene.gene_id().c_str(),
+                '-',
+                "-",
+                gene.gene_id().c_str(),
+                gene_names.c_str(), 
+                tss_ids.c_str(),
+                rt.get_name(bundle.ref_id()),
+                gene.left(),
+                gene.right(),
+                "-",
+                "-",
+                status,
+                gene.FPKM(),
+                gene.confidence().low,
+                gene.confidence().high);
 		fflush(fgene_abundances);
 	}
-    
+    delete hit_introns;
 	//fprintf(fbundle_tracking, "CLOSE %d\n", bundle.id());
 	
-	if (ref_driven && num_scaffs_reported > bundle.ref_scaffolds().size())
+	if (bundle_mode==REF_DRIVEN && num_scaffs_reported > bundle.ref_scaffolds().size())
     {
 		fprintf(stderr, "Error: reported more isoforms than in reference!\n");
 		exit(1);
 	}
 	
-    asm_verbose( "%s\tBundle complete\n", bundle_label->c_str());
+    verbose_msg( "%s\tBundle complete\n", bundle_label->c_str());
     
 #if ENABLE_THREADS
 	out_file_lock.unlock();
@@ -866,33 +1257,41 @@ void assemble_bundle(const RefSequenceTable& rt,
 
 bool assemble_hits(BundleFactory& bundle_factory, BiasLearner* bl_ptr)
 {
-	srand(time(0));
-	
-	int num_bundles = 0;
-	
+	//srand(time(0));
+		
 	RefSequenceTable& rt = bundle_factory.ref_table();
     
 	//FILE* fbundle_tracking = fopen("open_bundles", "w");
     
 	//FILE* fstats = fopen("bundles.stats", "w");
-	FILE* ftrans_abundances = fopen(string(output_dir + "/" + "transcripts.expr").c_str(), "w");
-	fprintf(ftrans_abundances,"trans_id\tbundle_id\tchr\tleft\tright\tFPKM\tFMI\tfrac\tFPKM_conf_lo\tFPKM_conf_hi\tcoverage\tlength\teffective_length\tstatus\n");
-	
-	FILE* fgene_abundances = fopen(string(output_dir + "/" + "genes.expr").c_str(), "w");
-	fprintf(fgene_abundances,"gene_id\tbundle_id\tchr\tleft\tright\tFPKM\tFPKM_conf_lo\tFPKM_conf_hi\tstatus\n");
+	FILE* ftrans_abundances = fopen(string(output_dir + "/" + "isoforms.fpkm_tracking").c_str(), "w");
+	//fprintf(ftrans_abundances,"trans_id\tbundle_id\tchr\tleft\tright\tFPKM\tFMI\tfrac\tFPKM_conf_lo\tFPKM_conf_hi\tcoverage\tlength\teffective_length\tstatus\n");
+	fprintf(ftrans_abundances,"tracking_id\tclass_code\tnearest_ref_id\tgene_id\tgene_short_name\ttss_id\tlocus\tlength\tcoverage\tstatus\tFPKM\tFPKM_conf_lo\tFPKM_conf_hi\n");
+	FILE* fgene_abundances = fopen(string(output_dir + "/" + "genes.fpkm_tracking").c_str(), "w");
+	//fprintf(fgene_abundances,"gene_id\tbundle_id\tchr\tleft\tright\tFPKM\tFPKM_conf_lo\tFPKM_conf_hi\tstatus\n");
+    fprintf(fgene_abundances,"tracking_id\tclass_code\tnearest_ref_id\tgene_id\tgene_short_name\ttss_id\tlocus\tlength\tcoverage\tstatus\tFPKM\tFPKM_conf_lo\tFPKM_conf_hi\n");
+    
 	FILE* ftranscripts = fopen(string(output_dir + "/" + "transcripts.gtf").c_str(), "w");
     
 	string process;
-	if (corr_bias && final_est_run)
+	if (corr_bias && corr_multi && final_est_run)
+		process = "Re-estimating abundances with bias and multi-read correction.";
+	else if (corr_multi && final_est_run)
+		process = "Re-estimating abundances with multi-read correction.";
+	else if (corr_bias && final_est_run)
 		process = "Re-estimating abundances with bias correction.";
-	else if (ref_driven && final_est_run)
+	else if (bundle_mode==REF_DRIVEN && final_est_run)
 		process = "Estimating transcript abundances."; 
-	else if (ref_driven && corr_bias)
+	else if (bundle_mode==REF_DRIVEN && corr_bias)
 		process = "Learning bias parameters.";
+	else if (bundle_mode==REF_DRIVEN && corr_multi)
+		process = "Initializing transcript abundances for multi-read correction.";
+	else if (corr_multi)
+		process = "Assembling transcripts and initializing abundances for multi-read correction.";
 	else
 		process = "Assembling transcripts and estimating abundances.";
 		
-	ProgressBar p_bar(process, bundle_factory.num_bundles());
+	ProgressBar p_bar(process, bundle_factory.read_group_properties()->total_map_mass());
 
 	while(true)
 	{
@@ -905,7 +1304,7 @@ bool assemble_hits(BundleFactory& bundle_factory, BiasLearner* bl_ptr)
 		}
 		
 		HitBundle& bundle = *bundle_ptr;
-
+        
 		char bundle_label_buf[2048];
 		sprintf(bundle_label_buf, 
 				"%s:%d-%d", 
@@ -913,13 +1312,14 @@ bool assemble_hits(BundleFactory& bundle_factory, BiasLearner* bl_ptr)
 				bundle.left(),
 				bundle.right());
 
-		if (bundle.right() - bundle.left() > 3000000)
+		if (bundle.right() - bundle.left() > max_gene_length)
 		{
-			verbose_msg( "%s\tWarning: large bundle encountered\n", bundle_label_buf);
+			fprintf(stderr, "\n%s\tWarning: Skipping large bundle.\n", bundle_label_buf);
+			delete bundle_ptr;
+			continue;
 		}
 
 		BundleStats stats;
-		num_bundles++;
 #if ENABLE_THREADS			
 		while(1)
 		{
@@ -936,7 +1336,7 @@ bool assemble_hits(BundleFactory& bundle_factory, BiasLearner* bl_ptr)
 			
 		}
 #endif
-		p_bar.update(bundle_label_buf, 1);	
+		p_bar.update(bundle_label_buf, bundle.raw_mass());	
 
 #if ENABLE_THREADS			
 		thread_pool_lock.lock();
@@ -947,7 +1347,7 @@ bool assemble_hits(BundleFactory& bundle_factory, BiasLearner* bl_ptr)
 					 boost::cref(rt), 
 					 bundle_ptr, 
 					 bl_ptr,
-					 bundle_factory.read_group_properties()->total_map_mass(),
+					 bundle_factory.read_group_properties()->normalized_map_mass(),
 					 ftranscripts, 
 					 fgene_abundances,
 					 ftrans_abundances);
@@ -955,7 +1355,7 @@ bool assemble_hits(BundleFactory& bundle_factory, BiasLearner* bl_ptr)
 		assemble_bundle(boost::cref(rt), 
 						bundle_ptr, 
 						bl_ptr,
-						bundle_factory.read_group_properties()->total_map_mass(),
+						bundle_factory.read_group_properties()->normalized_map_mass(),
 						ftranscripts,
 						fgene_abundances,
 						ftrans_abundances);
@@ -972,6 +1372,7 @@ bool assemble_hits(BundleFactory& bundle_factory, BiasLearner* bl_ptr)
 			thread_pool_lock.unlock();
 			break;
 		}
+		p_bar.remaining(curr_threads);
 		
 		thread_pool_lock.unlock();
 		//fprintf(stderr, "waiting to exit\n");
@@ -981,21 +1382,24 @@ bool assemble_hits(BundleFactory& bundle_factory, BiasLearner* bl_ptr)
 	
 	p_bar.complete();
 	
-	if(!final_est_run && ref_driven) // We are learning bias
+	if(!final_est_run && bundle_mode==REF_DRIVEN) // We are learning bias
 	{
 		bl_ptr->normalizeParameters();
-#if ADAM_MODE
-		bl_ptr->output();
-#endif
+        if (output_bias_params)
+            bl_ptr->output();
 	}
+	
+	fclose(ftranscripts);
+	fclose(ftrans_abundances);
+	fclose(fgene_abundances);
 	return true;
 }
 	
 void driver(const string& hit_file_name, FILE* ref_gtf, FILE* mask_gtf)
-{
-	ReadTable it;
+{	
+    ReadTable it;
 	RefSequenceTable rt(true, false);
-
+	    
 	shared_ptr<HitFactory> hit_factory;
 
     try
@@ -1018,10 +1422,9 @@ void driver(const string& hit_file_name, FILE* ref_gtf, FILE* mask_gtf)
             exit(1);
         }
 	}
-	BundleFactory& bundle_factory = *(new BundleFactory(hit_factory));
 	
-	shared_ptr<EmpDist> frag_len_dist(new EmpDist);
-	long double map_mass = 0.0;
+	BundleFactory& bundle_factory = *(new BundleFactory(hit_factory, bundle_mode));
+	shared_ptr<ReadGroupProperties> rg_props =bundle_factory.read_group_properties();
 	BadIntronTable bad_introns;
     
     rt.print_rec_ordering();
@@ -1029,7 +1432,7 @@ void driver(const string& hit_file_name, FILE* ref_gtf, FILE* mask_gtf)
     vector<shared_ptr<Scaffold> > ref_mRNAs;
     if (ref_gtf)
     {
-        ::load_ref_rnas(ref_gtf, bundle_factory.ref_table(), ref_mRNAs, corr_bias, false);
+        ::load_ref_rnas(ref_gtf, bundle_factory.ref_table(), ref_mRNAs, corr_bias && bundle_mode == REF_DRIVEN, false);
         bundle_factory.set_ref_rnas(ref_mRNAs);
     }
     rt.print_rec_ordering();
@@ -1040,14 +1443,15 @@ void driver(const string& hit_file_name, FILE* ref_gtf, FILE* mask_gtf)
         bundle_factory.set_mask_rnas(mask_rnas);
     }
     
-    if (ref_driven)
-        inspect_map(bundle_factory, map_mass, NULL, *frag_len_dist);
+    vector<pair<string, double> > count_table;
+    if (bundle_mode != HIT_DRIVEN)
+        inspect_map(bundle_factory, NULL, count_table);
     else 
-        inspect_map(bundle_factory, map_mass, &bad_introns, *frag_len_dist);
+        inspect_map(bundle_factory, &bad_introns, count_table);
     
     
-    asm_verbose("%d ReadHits still live\n", num_deleted);
-    asm_verbose("Found %lu reference contigs\n", rt.size());
+    verbose_msg("%d ReadHits still live\n", num_deleted);
+    verbose_msg("Found %lu reference contigs\n", rt.size());
     
     foreach(shared_ptr<Scaffold> ref_scaff, ref_mRNAs)
     {
@@ -1055,30 +1459,18 @@ void driver(const string& hit_file_name, FILE* ref_gtf, FILE* mask_gtf)
     }
     
     //fprintf(stderr, "ReadHit delete count is %d\n", num_deleted);
-    shared_ptr<ReadGroupProperties> rg_props(new ReadGroupProperties);
-    
-    if (global_read_properties)
-    {
-        *rg_props = *global_read_properties;
-    }
-    else 
-    {
-        *rg_props = hit_factory->read_group_properties();
-    }
     
-    rg_props->frag_len_dist(frag_len_dist);
-    rg_props->total_map_mass(map_mass);
 	BiasLearner* bl_ptr = new BiasLearner(rg_props->frag_len_dist());
     bundle_factory.read_group_properties(rg_props);
 
-	if (ref_driven)
+	if (ref_gtf)
 		bundle_factory.bad_intron_table(bad_introns);
 	
-	max_frag_len = frag_len_dist->max();
-	min_frag_len = frag_len_dist->min();
-	verbose_msg("\tTotal map density: %Lf\n", map_mass);
+	max_frag_len = rg_props->frag_len_dist()->max();
+	min_frag_len = rg_props->frag_len_dist()->min();
+	verbose_msg("\tTotal map density: %Lf\n", rg_props->total_map_mass());
 
-	if (corr_bias) final_est_run = false;
+	if (corr_bias || corr_multi) final_est_run = false;
 
 	assemble_hits(bundle_factory, bl_ptr);
     
@@ -1089,17 +1481,17 @@ void driver(const string& hit_file_name, FILE* ref_gtf, FILE* mask_gtf)
     }
     
 	hit_factory->reset();
-	int num_bundles = bundle_factory.num_bundles();
 	delete &bundle_factory;
-	BundleFactory bundle_factory2(hit_factory);
+	BundleFactory bundle_factory2(hit_factory, REF_DRIVEN);
 	rg_props->bias_learner(shared_ptr<BiasLearner const>(bl_ptr));
-	bundle_factory2.num_bundles(num_bundles);
+	rg_props->multi_read_table()->valid_mass(true);
+	bundle_factory2.read_group_properties(rg_props);
 
-    if (!ref_gtf)
+    if (bundle_mode==HIT_DRIVEN || bundle_mode==REF_GUIDED)
     {
 		ref_gtf = fopen(string(output_dir + "/transcripts.gtf").c_str(), "r");
         ref_mRNAs.clear();
-        ::load_ref_rnas(ref_gtf, bundle_factory2.ref_table(), ref_mRNAs, true, true);
+        ::load_ref_rnas(ref_gtf, bundle_factory2.ref_table(), ref_mRNAs, corr_bias, true);
     }    
 	bundle_factory2.set_ref_rnas(ref_mRNAs);
     if (mask_gtf)
@@ -1108,39 +1500,51 @@ void driver(const string& hit_file_name, FILE* ref_gtf, FILE* mask_gtf)
         ::load_ref_rnas(mask_gtf, bundle_factory2.ref_table(), mask_rnas, false, false);
         bundle_factory2.set_mask_rnas(mask_rnas);
     }    
-    bundle_factory2.read_group_properties(rg_props);
 	bundle_factory2.reset();
 	
-	if(!ref_driven) // We still need to learn the bias since we didn't have the sequences before assembly
+	if(corr_bias && (bundle_mode==HIT_DRIVEN || bundle_mode==REF_GUIDED)) 
 	{
+        // We still need to learn the bias since we didn't have the sequences before assembly
 		learn_bias(bundle_factory2, *bl_ptr);
 		bundle_factory2.reset();
 	}
 
+    bundle_mode = REF_DRIVEN;
 	final_est_run = true;
 	assemble_hits(bundle_factory2, bl_ptr);
 	ref_mRNAs.clear();
 }
 
 int main(int argc, char** argv)
-{
+{	
     init_library_table();
     
 	int parse_ret = parse_options(argc,argv);
     if (parse_ret)
         return parse_ret;
 	
+    if (!use_total_mass && !use_compat_mass)
+    {
+        use_total_mass = true;
+        use_compat_mass = false;   
+    }
+    
     if(optind >= argc)
     {
         print_usage();
         return 1;
     }
 	
+    if (!no_update_check)
+        check_version(PACKAGE_VERSION);
+    
     string sam_hits_file_name = argv[optind++];
 	
 
-	
-	srand48(time(NULL));
+	if (random_seed == -1)
+        random_seed = time(NULL);
+    
+	srand48(random_seed);
 	
 	FILE* ref_gtf = NULL;
 	if (ref_gtf_filename != "")
diff --git a/src/cuffmerge.py b/src/cuffmerge.py
new file mode 100755
index 0000000..4c2c285
--- /dev/null
+++ b/src/cuffmerge.py
@@ -0,0 +1,564 @@
+#!/usr/bin/env python
+# encoding: utf-8
+"""
+cuffmerge.py
+
+Created by Cole Trapnell on 2011-03-17.
+Copyright (c) 2011 Cole Trapnell. All rights reserved.
+"""
+
+import sys
+import getopt
+from datetime import datetime, date, time
+import shutil
+import subprocess
+import errno
+import os
+import tempfile
+import warnings
+import types
+
+help_message = '''
+cuffmerge takes two or more Cufflinks GTF files and merges them into a
+single unified transcript catalog.  Optionally, you can provide the script
+with a reference GTF, and the script will use it to attach gene names and other
+metadata to the merged catalog.
+
+Usage:
+    cuffmerge [Options] <assembly_GTF_list.txt>
+
+Options:
+    -h/--help                               Prints the help message and exits
+    -o                     <output_dir>     Directory where merged assembly will be written  [ default: ./merged_asm  ]
+    -g/--ref-gtf                            An optional "reference" annotation GTF.
+    -s/--ref-sequence      <seq_dir>/<seq_fasta> Genomic DNA sequences for the reference.
+    --min-isoform-fraction <0-1.0>          Discard isoforms with abundance below this       [ default:           0.5 ]
+    -p/--num-threads       <int>            Use this many threads to merge assemblies.       [ default:             1 ]
+    --keep-tmp                              Keep all intermediate files during merge
+'''
+
+output_dir = "./merged_asm/"
+logging_dir = output_dir + "/logs/merge_asm_logs/"
+run_log = None
+run_cmd = None
+tmp_dir = output_dir + "/meta_asm_tmp/"
+bin_dir = sys.path[0] + "/"
+run_meta_assembly = True
+fail_str = "\t[FAILED]\n"
+params = None
+
+class Usage(Exception):
+    def __init__(self, msg):
+        self.msg = msg
+
+class TestParams:
+
+    class SystemParams:
+        def __init__(self,
+                     threads,
+                     keep_tmp):
+            self.threads = threads
+            self.keep_tmp = keep_tmp
+
+        def parse_options(self, opts):
+            for option, value in opts:
+                if option in ("-p", "--num-threads"):
+                    self.threads = int(value)
+                if option in ("--keep-tmp"):
+                    self.keep_tmp = True
+
+        def check(self):
+            pass
+
+    def __init__(self):
+        self.system_params = self.SystemParams(1,               # threads
+                                               False)           # keep_tmp
+        self.ref_gtf = None
+        self.fasta = None
+        self.min_isoform_frac = 0.05
+
+    def check(self):
+        self.system_params.check()
+
+    def parse_options(self, argv):
+        try:
+            opts, args = getopt.getopt(argv[1:],
+                                       "hvp:o:g:M:s:q:F:",
+                                       ["version",
+                                        "help",
+                                        "ref-sequence=",
+                                        "ref-gtf=",
+                                        "output-dir=",
+                                        "num-threads=",
+                                        "keep-tmp",
+                                        "min-isoform-fraction="])
+        except getopt.error, msg:
+            raise Usage(msg)
+
+        self.system_params.parse_options(opts)
+
+        global output_dir
+        global logging_dir
+        global tmp_dir
+
+        # option processing
+        for option, value in opts:
+            if option in ("-v", "--version"):
+                print "merge_cuff_asms v%s" % (get_version())
+                exit(0)
+            if option in ("-h", "--help"):
+                raise Usage(help_message)
+            if option in ("-g", "--ref-gtf"):
+                self.ref_gtf = value
+            if option in ("-s", "--ref-sequence"):
+                self.fasta = value
+            if option in ("-F", "--min-isoform-fraction"):
+                self.min_isoform_frac = float(value)
+            if option in ("-o", "--output-dir"):
+                output_dir = value + "/"
+                logging_dir = output_dir + "logs/"
+                tmp_dir = output_dir + "tmp/"
+
+        return args
+
+
+def right_now():
+    curr_time = datetime.now()
+    return curr_time.strftime("%c")
+
+def prepare_output_dir():
+
+    print >> sys.stderr, "[%s] Preparing output location %s" % (right_now(), output_dir)
+    if os.path.exists(output_dir):
+        pass
+    else:
+        os.makedirs(output_dir)
+
+    #print >> sys.stderr, "Checking for %s", logging_dir
+    if os.path.exists(logging_dir):
+        pass
+    else:
+        #print >> sys.stderr, "Creating %s", logging_dir
+        os.makedirs(logging_dir)
+
+    if os.path.exists(tmp_dir):
+        pass
+    else:
+        os.makedirs(tmp_dir)
+
+def formatTD(td):
+    hours = td.seconds // 3600
+    minutes = (td.seconds % 3600) // 60
+    seconds = td.seconds % 60
+    return '%02d:%02d:%02d' % (hours, minutes, seconds)
+
+def tmp_name(prefix):
+    tmp_root = output_dir + "tmp/"
+    if os.path.exists(tmp_root):
+        pass
+    else:
+        os.mkdir(tmp_root)
+    return tmp_root + prefix + os.tmpnam().split('/')[-1]
+
+def cufflinks(out_dir,
+              sam_file,
+              min_isoform_frac,
+              gtf_file=None,
+              extra_opts=["-q", "--overhang-tolerance", "200", "--library-type=transfrags",  "-A","0.0", "--min-frags-per-transfrag", "0"],
+              lsf=False,
+              curr_queue=None):
+    if gtf_file != None:
+        print >> sys.stderr, "[%s] Quantitating transcripts" % (right_now())
+    else:
+        print >> sys.stderr, "[%s] Assembling transcripts" % (right_now())
+
+    cmd = ["cufflinks"]
+
+    if out_dir != None and out_dir != "":
+        cmd.extend(["-o", out_dir])
+
+    cmd.extend(["-F", str(min_isoform_frac)])
+
+    if gtf_file != None:
+        cmd.extend(["-g", gtf_file])
+
+    if extra_opts != None:
+        cmd.extend(extra_opts)
+    global params
+    # Run Cufflinks with more than one thread?
+    cmd.extend(["-p", str(params.system_params.threads)])
+
+    cmd.append(sam_file)
+
+    try:
+        print >> run_log, " ".join(cmd)
+        ret = subprocess.call(cmd)
+        if ret != 0:
+            print >> sys.stderr, fail_str, "Error: could not execute cufflinks"
+            exit(1)
+    # cufflinks not found
+    except OSError, o:
+        if o.errno == errno.ENOTDIR or o.errno == errno.ENOENT:
+            print >> sys.stderr, fail_str, "Error: cufflinks not found on this system.  Did you forget to include it in your PATH?"
+        exit(1)
+
+def cuffcompare(prefix, ref_gtf, fasta, cuff_gtf):
+
+    print >> sys.stderr, "[%s] Comparing reference %s to assembly %s" % (right_now(), ref_gtf, cuff_gtf)
+    cmd = ["cuffcompare"]
+
+    if  prefix != None:
+        cmd.extend(["-o", prefix])
+    if  ref_gtf != None:
+        cmd.extend(["-r", ref_gtf])
+    if  fasta != None:
+        cmd.extend(["-s", fasta])
+    if type(cuff_gtf) == types.ListType:
+        for g in cuff_gtf:
+            cmd.extend([g])
+    else:
+        cmd.extend([cuff_gtf])
+
+    try:
+        print >> run_log, " ".join(cmd)
+        ret = subprocess.call(cmd)
+        if ret != 0:
+            print >> sys.stderr, fail_str, "Error: could not execute cuffcompare"
+            exit(1)
+    # cuffcompare not found
+    except OSError, o:
+        if o.errno == errno.ENOTDIR or o.errno == errno.ENOENT:
+            print >> sys.stderr, fail_str, "Error: cuffcompare not found on this system.  Did you forget to include it in your PATH?"
+        exit(1)
+
+def gtf_to_sam(gtf_filename):
+
+    sam_out = tmp_name("gtf2sam_")
+
+    cmd = ["gtf_to_sam"]
+    cmd.append("-F")
+    cmd.append(gtf_filename)
+    cmd.append(sam_out)
+    try:
+        print >> run_log, " ".join(cmd)
+        ret = subprocess.call(cmd)
+        if ret != 0:
+            print >> sys.stderr, fail_str, "Error: could not execute gtf_to_sam"
+            exit(1)
+    # gtf_to_sam not found
+    except OSError, o:
+        if o.errno == errno.ENOTDIR or o.errno == errno.ENOENT:
+            print >> sys.stderr, fail_str, "Error: gtf_to_sam not found on this system.  Did you forget to include it in your PATH?"
+        exit(1)
+    return sam_out
+
+def test_input_files(filename_list):
+    """This function takes a file that contains a list of GTF files,
+       tests accessibility of each, and returns the list of filenames"""
+
+    OK = True
+    input_files = []
+    for line in filename_list:
+        line = line.strip()
+
+        # Skip comment line
+        if len(line) == 0 or line[0] == "#":
+            continue
+        try:
+            g = open(line,"r")
+            input_files.append(line)
+
+        except OSError, o:
+            if o.errno == errno.ENOTDIR or o.errno == errno.ENOENT:
+                print >> sys.stderr, fail_str, "Error: could not open %s" % line
+            OK = False
+    if not OK:
+        sys.exit(1)
+    return input_files
+
+def convert_gtf_to_sam(gtf_filename_list):
+    """This function takes a list of GTF files, converts them all to
+       temporary SAM files, and returns the list of temporary file names."""
+    print >> sys.stderr, "[%s] Converting GTF files to SAM" % (right_now())
+    OK = True
+    sam_input_filenames = []
+    for line in gtf_filename_list:
+        try:
+            sam_out = gtf_to_sam(line)
+            sam_input_filenames.append(sam_out)
+        except OSError, o:
+            if o.errno == errno.ENOTDIR or o.errno == errno.ENOENT:
+                print >> sys.stderr, fail_str, "Error: could not open %s" % line
+            OK = False
+    if not OK:
+        sys.exit(1)
+    return sam_input_filenames
+
+def merge_sam_inputs(sam_input_list, header):
+    sorted_map_name = tmp_name( "mergeSam_")
+
+    sorted_map = open(sorted_map_name, "w")
+
+    #print header
+
+    # The header was built from a dict keyed by chrom, so
+    # the records will be lexicographically ordered and
+    # should match the BAM after the sort below.
+    print >> sorted_map, header,
+
+    sorted_map.close()
+    sort_cmd =["sort",
+               "-k",
+               "3,3",
+               "-k",
+               "4,4n",
+               "--temporary-directory="+tmp_dir]
+    sort_cmd.extend(sam_input_list)
+
+    print >> run_log, " ".join(sort_cmd), ">", sorted_map_name
+    subprocess.call(sort_cmd,
+                   stdout=open(sorted_map_name, "a"))
+    return sorted_map_name
+
+def compare_to_reference(meta_asm_gtf, ref_gtf, fasta):
+    print >> sys.stderr, "[%s] Comparing against reference file %s" % (right_now(), ref_gtf)
+    ref_str = ""
+    if ref_gtf != None:
+        ref_str = " -r %s " % ref_gtf
+
+    if fasta != None:
+        comp_cmd = '''cuffcompare -o tmp_meta_asm %s -s %s %s %s''' % (ref_str, fasta, meta_asm_gtf, meta_asm_gtf)
+    else:
+        comp_cmd = '''cuffcompare -o tmp_meta_asm %s %s %s''' % (ref_str, meta_asm_gtf, meta_asm_gtf)
+
+    #cmd = bsub_cmd(comp_cmd, "/gencode_cmp", True, job_mem=8)
+    cmd = comp_cmd
+
+    try:
+        print >> run_log, cmd
+        ret = subprocess.call(cmd,shell=True)
+        if ret != 0:
+            print >> sys.stderr, fail_str, "Error: could not execute cuffcompare"
+            exit(1)
+        #tmap_out = meta_asm_gtf.split("/")[-1] + ".tmap"
+        tfpath, tfname = os.path.split(meta_asm_gtf)
+        if tfpath: tfpath+='/'
+        tmap_out = tfpath+'tmp_meta_asm.'+tfname+".tmap"
+        return tmap_out
+    # cuffcompare not found
+    except OSError, o:
+        if o.errno == errno.ENOTDIR or o.errno == errno.ENOENT:
+            print >> sys.stderr, fail_str, "Error: cuffcompare not found on this system.  Did you forget to include it in your PATH?"
+        exit(1)
+
+def select_gtf(gtf_in_filename, ids, gtf_out_filename):
+    f_gtf = open(gtf_in_filename)
+    #print >> sys.stderr, "Select GTF: Ids are: "
+    #print >> sys.stderr, ids
+    #print >> sys.stderr, "reference gtf file name:"
+    #print >> sys.stderr, gtf_in_filename
+    out_gtf = open(gtf_out_filename, "w")
+    for line in f_gtf:
+        line = line.strip()
+        cols = line.split('\t')
+        if len(cols) < 9:
+            continue
+        attrs = cols[8]
+        attr_cols = attrs.split(';')
+        for col in attr_cols:
+            if col.find("transcript_id") != -1:
+                first_quote = col.find('"')
+                last_quote = col.find('"', first_quote + 1)
+                transcript = col[first_quote + 1:last_quote]
+                #print >> sys.stderr, transcript
+                if transcript in ids:
+                    print >> out_gtf, line
+
+
+def merge_gtfs(gtf_filenames, merged_gtf, ref_gtf=None):
+    print >> sys.stderr, "[%s] Merging linc gtf files with cuffcompare" % (right_now())
+    cmd = ["cuffcompare"]
+
+    cmd.extend(["-o", merged_gtf])
+    if ref_gtf != None:
+        cmd.extend(["-r", ref_gtf])
+
+    cmd.extend(gtf_filenames)
+    cmd = " ".join(cmd)
+    #cmd = bsub_cmd(cmd, "/merge_gtf", True, job_mem=8)
+
+    try:
+        print >> run_log, cmd
+        ret = subprocess.call(cmd, shell=True)
+        if ret != 0:
+            print >> sys.stderr, fail_str, "Error: could not execute cuffcompare"
+            exit(1)
+        return merged_gtf + ".combined.gtf"
+    # cuffcompare not found
+    except OSError, o:
+        if o.errno == errno.ENOTDIR or o.errno == errno.ENOENT:
+            print >> sys.stderr, fail_str, "Error: cuffcompare not found on this system.  Did you forget to include it in your PATH?"
+        exit(1)
+
+def compare_meta_asm_against_ref(ref_gtf, fasta_file, gtf_input_file, class_codes=["c", "i", "r", "p", "e"]):
+    #print >> sys.stderr, "Cuffcmpare all assemblies GTFs"
+
+    tmap = compare_to_reference(gtf_input_file, ref_gtf, fasta_file)
+
+    #print >> sys.stderr, "Cuffcmpare all assemblies GTFs : filter %s" % ",".join(class_codes)
+    selected_ids= set([])
+    f_tmap = open(tmap)
+    #out = open("tmp_meta_asm_selectedIds.txt", "w")
+    for line in f_tmap:
+        line = line.strip()
+        cols = line.split('\t')
+        if len(cols) < 5:
+            continue
+        class_code = cols[2]
+        name = cols[4]
+        if class_code not in class_codes:
+            selected_ids.add(name)
+
+    global output_dir
+    asm_dir = output_dir
+
+    if os.path.exists(asm_dir):
+        pass
+    else:
+        os.mkdir(asm_dir)
+    current_asm_gtf = output_dir +"transcripts.gtf"
+    select_gtf(current_asm_gtf, selected_ids, output_dir + "/merged.gtf")
+    mtmap = compare_to_reference(output_dir + "/merged.gtf", ref_gtf, fasta_file)
+    os.remove(mtmap)
+    os.remove(mtmap.split(".tmap")[0]+".refmap")
+    shutil.move("tmp_meta_asm.combined.gtf", output_dir + "/merged.gtf")
+
+#    os.remove("tmp_meta_asm.combined.gtf")
+    os.remove("tmp_meta_asm.loci")
+    os.remove("tmp_meta_asm.tracking")
+    os.remove("tmp_meta_asm.stats")
+    os.remove(tmap)
+    os.remove(tmap.split(".tmap")[0]+".refmap")
+    #tmp_dir = asm_dir
+    #tmp_files = os.listdir(tmp_dir)
+    #for t in tmp_files:
+    #    os.remove(tmp_dir+t)
+    #os.rmdir(tmp_dir)
+
+#os.remove("tmp_meta_asm.tmap")
+
+def get_version():
+    return "1.0.0"
+
+def get_gtf_chrom_info(gtf_filename, known_chrom_info=None):
+    gtf_file = open(gtf_filename)
+    if known_chrom_info == None:
+        chroms = {}
+    else:
+        chroms = known_chrom_info
+    for line in gtf_file:
+        line = line.strip()
+        if line[0] == "#":
+            continue
+        cols = line.split('\t')
+        if len(cols) < 8:
+            continue
+        chrom = cols[0]
+        left = int(cols[3])
+        right = int(cols[4])
+        bounds = chroms.setdefault(chrom, [9999999999,-1])
+        if bounds[0] > left:
+            bounds[0] = left
+        if bounds[1] < right:
+            bounds[1] = right
+    return chroms
+
+def header_for_chrom_info(chrom_info):
+    header_strs = ["""@HD\tVN:1.0\tSO:coordinate"""]
+    for chrom, limits in chrom_info.iteritems():
+        line = "@SQ\tSN:%s\tLN:\t%d" % (chrom, limits[1])
+        header_strs.append(line)
+    header_strs.append("@PG\tID:cuffmerge\tVN:1.0.0\n")
+    header = "\n".join(header_strs)
+    return header
+
+def main(argv=None):
+
+    warnings.filterwarnings("ignore", "tmpnam is a potential security risk")
+    global params
+    params = TestParams()
+
+    try:
+        if argv is None:
+            argv = sys.argv
+            args = params.parse_options(argv)
+            params.check()
+
+        #if len(args) < 2:
+        #    raise Usage(help_message)
+
+        global run_log
+        global run_cmd
+
+        print >> sys.stderr
+        print >> sys.stderr, "[%s] Beginning transcriptome assembly merge" % (right_now())
+        print >> sys.stderr, "-------------------------------------------"
+        print >> sys.stderr
+
+        start_time = datetime.now()
+        prepare_output_dir()
+
+        run_log = open(logging_dir + "run.log", "w", 0)
+        run_cmd = " ".join(argv)
+        print >> run_log, run_cmd
+
+        if len(args) < 1:
+            raise(Usage(help_message))
+
+        transfrag_list_file = open(args[0], "r")
+
+        if params.ref_gtf != None:
+            test_input_files([params.ref_gtf])
+        else:
+            print >> sys.stderr, "Warning: no reference GTF provided!"
+
+        # Check that all the primary assemblies are accessible before starting the time consuming stuff
+        gtf_input_files = test_input_files(transfrag_list_file)
+
+        all_gtfs = []
+        all_gtfs.extend(gtf_input_files)
+        if params.ref_gtf != None:
+            all_gtfs.append(params.ref_gtf)
+        chrom_info = {}
+        for gtf in all_gtfs:
+            chrom_info = get_gtf_chrom_info(gtf, chrom_info)
+
+        header = header_for_chrom_info(chrom_info)
+
+        #Meta assembly option:
+        global run_meta_assembly
+        if run_meta_assembly:
+            # Convert the primary assemblies to SAM format
+            sam_input_files = convert_gtf_to_sam(gtf_input_files)
+            # Merge the primary assembly SAMs into a single input SAM file
+            merged_sam_filename = merge_sam_inputs(sam_input_files, header)
+            # Run cufflinks on the primary assembly transfrags to generate a meta-assembly
+            cufflinks(output_dir, merged_sam_filename, params.min_isoform_frac, params.ref_gtf)
+            compare_meta_asm_against_ref(params.ref_gtf, params.fasta, output_dir+"/transcripts.gtf")
+        #Meta Cuffcompare option:
+        else:
+            cuffcompare_all_assemblies(gtf_input_files) #FIXME: where is this function ?
+            
+
+        if not params.system_params.keep_tmp:
+            tmp_files = os.listdir(tmp_dir)
+            for t in tmp_files:
+                os.remove(tmp_dir+t)
+            os.rmdir(tmp_dir)
+    except Usage, err:
+        print >> sys.stderr, sys.argv[0].split("/")[-1] + ": " + str(err.msg)
+        return 2
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/src/differential.cpp b/src/differential.cpp
index 7e6776a..a9d59bc 100644
--- a/src/differential.cpp
+++ b/src/differential.cpp
@@ -14,10 +14,150 @@
 #include "abundances.h"
 #include "differential.h"
 #include "clustering.h"
+#include "differential.h"
 
 using namespace std;
 
-double min_read_count = 1000;
+double min_read_count = 10;
+
+#if ENABLE_THREADS
+mutex _launcher_lock;
+mutex locus_thread_pool_lock;
+int locus_curr_threads = 0;
+int locus_num_threads = 0;
+
+void decr_pool_count()
+{
+	locus_thread_pool_lock.lock();
+	locus_curr_threads--;
+	locus_thread_pool_lock.unlock();	
+}
+#endif
+
+TestLauncher::launcher_sample_table::iterator TestLauncher::find_locus(const string& locus_id)
+{
+    launcher_sample_table::iterator itr = _samples.begin();
+    for(; itr != _samples.end(); ++itr)
+    {
+        if (itr->first == locus_id)
+            return itr;
+    }
+    return _samples.end();
+}
+
+void TestLauncher::register_locus(const string& locus_id)
+{
+#if ENABLE_THREADS
+	boost::mutex::scoped_lock lock(_launcher_lock);
+#endif	
+    
+    launcher_sample_table::iterator itr = find_locus(locus_id);
+    if (itr == _samples.end())
+    {
+        pair<launcher_sample_table::iterator, bool> p;
+        vector<shared_ptr<SampleAbundances> >abs(_orig_workers);
+        _samples.push_back(make_pair(locus_id, abs));
+    }
+}
+
+void TestLauncher::abundance_avail(const string& locus_id, 
+                                   shared_ptr<SampleAbundances> ab, 
+                                   size_t factory_id)
+{
+#if ENABLE_THREADS
+	boost::mutex::scoped_lock lock(_launcher_lock);
+#endif	
+    launcher_sample_table::iterator itr = find_locus(locus_id);
+    if (itr == _samples.end())
+    {
+        assert(false);
+    }
+    itr->second[factory_id] = ab;
+    //itr->second(factory_id] = ab;
+}
+
+// Note: this routine should be called under lock - it doesn't
+// acquire the lock itself. 
+bool TestLauncher::all_samples_reported_in(vector<shared_ptr<SampleAbundances> >& abundances)
+{    
+    foreach (shared_ptr<SampleAbundances> ab, abundances)
+    {
+        if (!ab)
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
+// Note: this routine should be called under lock - it doesn't
+// acquire the lock itself. 
+void TestLauncher::perform_testing(vector<shared_ptr<SampleAbundances> >& abundances)
+{
+    assert (abundances.size() == _orig_workers);
+    
+    // Just verify that all the loci from each factory match up.
+    for (size_t i = 1; i < abundances.size(); ++i)
+    {
+        const SampleAbundances& curr = *(abundances[i]);
+        const SampleAbundances& prev = *(abundances[i-1]);
+        
+        assert (curr.locus_tag == prev.locus_tag);
+        
+        const AbundanceGroup& s1 = curr.transcripts;
+        const AbundanceGroup& s2 =  prev.transcripts;
+        
+        assert (s1.abundances().size() == s2.abundances().size());
+        
+        for (size_t j = 0; j < s1.abundances().size(); ++j)
+        {
+            assert (s1.abundances()[j]->description() == s2.abundances()[j]->description());
+        }
+    }
+    
+    test_differential(abundances.front()->locus_tag, abundances, *_tests, *_tracking, _samples_are_time_series);
+}
+
+void TestLauncher::test_finished_loci()
+{
+#if ENABLE_THREADS
+	boost::mutex::scoped_lock lock(_launcher_lock);
+#endif  
+
+    launcher_sample_table::iterator itr = _samples.begin(); 
+    while(itr != _samples.end())
+    {
+        if (all_samples_reported_in(itr->second))
+        {
+            // In some abundance runs, we don't actually want to perform testing 
+            // (eg initial quantification before bias correction).
+            // _tests and _tracking will be NULL in these cases.
+            if (_tests != NULL && _tracking != NULL)
+            {
+                if (_p_bar)
+                {
+                    verbose_msg("Testing for differential expression and regulation in locus [%s]\n", itr->second.front()->locus_tag.c_str());
+                    _p_bar->update(itr->second.front()->locus_tag.c_str(), 1);
+                }
+                perform_testing(itr->second);
+            }
+            else
+            {
+                if (_p_bar)
+                {
+                    //verbose_msg("Testing for differential expression and regulation in locus [%s]\n", abundances.front()->locus_tag.c_str());
+                    _p_bar->update(itr->second.front()->locus_tag.c_str(), 1);
+                }
+            }
+            itr = _samples.erase(itr);
+        }
+        else
+        {
+            
+            ++itr;
+        }
+    }
+}
 
 // This performs a between-group test on an isoform or TSS grouping, on two 
 // different samples.
@@ -28,37 +168,53 @@ bool test_diffexp(const FPKMContext& curr,
 	bool performed_test = false;
 	if (curr.FPKM > 0.0 && prev.FPKM > 0.0)
 	{
-		assert (curr.FPKM_variance > 0.0 && prev.FPKM_variance > 0.0);
+		//assert (curr.FPKM_variance > 0.0 && prev.FPKM_variance > 0.0);
 //		double log_curr = log(curr.counts);
 //		double log_prev = log(prev.counts);
         
-		double curr_log_fpkm_var = (curr.FPKM_variance) / (curr.FPKM * curr.FPKM);
-		double prev_log_fpkm_var = (prev.FPKM_variance) / (prev.FPKM * prev.FPKM);
+        double stat = 0.0;
+        double p_value = 1.0;
         
-        double numerator = log(prev.FPKM / curr.FPKM);
+        if (curr.FPKM_variance > 0.0 || prev.FPKM_variance > 0.0)
+        {
+            double curr_log_fpkm_var = (curr.FPKM_variance) / (curr.FPKM * curr.FPKM);
+            double prev_log_fpkm_var = (prev.FPKM_variance) / (prev.FPKM * prev.FPKM);
+            
+            double numerator = log(prev.FPKM / curr.FPKM);
+            
+            double denominator = sqrt(prev_log_fpkm_var + curr_log_fpkm_var);
+            stat = numerator / denominator;
         
-		double denominator = sqrt(prev_log_fpkm_var + curr_log_fpkm_var);
-		double stat = numerator / denominator;
 		
-		normal norm;
-		double t1, t2;
-		if (stat > 0.0)
-		{
-			t1 = stat;
-			t2 = -stat;
-		}
-		else
-		{
-			t1 = -stat;
-			t2 = stat;
-		}
-		double tail_1 = cdf(norm, t1);
-		double tail_2 = cdf(norm, t2);
+            normal norm;
+            double t1, t2;
+            if (stat > 0.0)
+            {
+                t1 = stat;
+                t2 = -stat;
+            }
+            else
+            {
+                t1 = -stat;
+                t2 = stat;
+            }
+            
+            if (isnan(t1) || isinf(t1) || isnan(t2) || isnan(t2))
+            {
+                
+                //fprintf(stderr, "Warning: test statistic is NaN! %s (samples %lu and %lu)\n", test.locus_desc.c_str(), test.sample_1, test.sample_2);
+                p_value = 1.0;
+            }
+            else
+            {
+                double tail_1 = cdf(norm, t1);
+                double tail_2 = cdf(norm, t2);
+                p_value = 1.0 - (tail_1 - tail_2);                
+            }
+        }
 		
 		double differential = log(curr.FPKM) - log(prev.FPKM);
 		
-		double p_value = 1.0 - (tail_1 - tail_2);
-		
 		//test = SampleDifference(sample1, sample2, prev.FPKM, curr.FPKM, stat, p_value, transcript_group_id);
 		test.p_value = p_value;
 		test.differential = differential;
@@ -70,25 +226,51 @@ bool test_diffexp(const FPKMContext& curr,
 	}
 	else
 	{
-		if (curr.FPKM > 0.0)
+		if (curr.FPKM > 0.0 )
 		{
-			//test = SampleDifference(sample1, sample2, 0, curr.FPKM, DBL_MAX, 0, transcript_group_id); 
-			test.p_value = 0;
-			test.differential = numeric_limits<double>::max();;
-			test.test_stat = numeric_limits<double>::max();
-			test.value_1 = 0;
-			test.value_2 = curr.FPKM;
-			performed_test = true;
+            if (curr.status != NUMERIC_LOW_DATA && curr.FPKM_variance > 0.0)
+            {
+                normal norm(curr.FPKM, sqrt(curr.FPKM_variance));
+                test.p_value = cdf(norm, 0);
+                performed_test = true;
+                test.differential = numeric_limits<double>::max();;
+                test.test_stat = numeric_limits<double>::max();
+                test.value_1 = 0;
+                test.value_2 = curr.FPKM;
+            }
+            else
+            {
+                test.differential = -numeric_limits<double>::max();
+                test.test_stat = -numeric_limits<double>::max();
+                test.value_1 = prev.FPKM;
+                test.value_2 = 0;
+                test.p_value = 1;
+                performed_test = false;
+            }
 		}
 		else if (prev.FPKM > 0.0)
 		{
-			//test = SampleDifference(sample1, sample2, prev.FPKM, 0, -DBL_MAX, 0, transcript_group_id); 
-			test.p_value = 0;
-			test.differential = -numeric_limits<double>::max();;
-			test.test_stat = -numeric_limits<double>::max();
-			test.value_1 = prev.FPKM;
-			test.value_2 = 0;
-			performed_test = true;
+            if (curr.status != NUMERIC_LOW_DATA &&  prev.FPKM_variance > 0.0)
+            {
+                normal norm(prev.FPKM, sqrt(prev.FPKM_variance));
+                test.p_value = cdf(norm, 0);
+                performed_test = true;
+                
+                test.differential = -numeric_limits<double>::max();
+                test.test_stat = -numeric_limits<double>::max();
+                test.value_1 = prev.FPKM;
+                test.value_2 = 0;
+            }
+            else
+            {
+                test.differential = -numeric_limits<double>::max();
+                test.test_stat = -numeric_limits<double>::max();
+                test.value_1 = prev.FPKM;
+                test.value_2 = 0;
+                test.p_value = 1;
+                performed_test = false;
+            }
+
 		}
 	}	
 	
@@ -96,6 +278,21 @@ bool test_diffexp(const FPKMContext& curr,
 	return performed_test;
 }
 
+SampleDiffMetaDataTable meta_data_table;
+#if ENABLE_THREADS
+boost::mutex meta_data_lock;
+#endif
+
+shared_ptr<SampleDifferenceMetaData> get_metadata(const string description)
+{
+#if ENABLE_THREADS
+    boost::mutex::scoped_lock lock(meta_data_lock);
+#endif
+    pair<SampleDiffMetaDataTable::iterator, bool> p;
+    p = meta_data_table.insert(make_pair(description, new SampleDifferenceMetaData()));
+    return p.first->second;
+}
+
 // This performs between-group tests on isoforms or TSS groupings in a single
 // locus, on two different samples.
 pair<int, SampleDiffs::iterator>  get_de_tests(const string& description,
@@ -107,6 +304,7 @@ pair<int, SampleDiffs::iterator>  get_de_tests(const string& description,
 	int total_iso_de_tests = 0;
 			
 	SampleDifference test;
+    
 	pair<SampleDiffs::iterator, bool> inserted;
 //	inserted = de_tests.insert(make_pair(curr_abundance.description(),
 //										 SampleDifference())); 
@@ -116,8 +314,28 @@ pair<int, SampleDiffs::iterator>  get_de_tests(const string& description,
     const FPKMContext& r1 = curr_abundance;
     const FPKMContext& r2 = prev_abundance;
     
-	if (curr_abundance.status == NUMERIC_OK && 
-		prev_abundance.status == NUMERIC_OK)
+	if (curr_abundance.status == NUMERIC_FAIL || 
+        prev_abundance.status == NUMERIC_FAIL)
+    {
+        test.test_stat = 0;
+		test.p_value = 1.0;
+		test.differential = 0.0;
+		test.test_status = FAIL;
+    }
+    else if (curr_abundance.status == NUMERIC_LOW_DATA && 
+             prev_abundance.status == NUMERIC_LOW_DATA)
+    {
+        // perform the test, but mark it as not significant and don't add it to the 
+        // pile. This way we don't penalize for multiple testing, but users can still
+        // see the fold change.
+		test_diffexp(r1, r2, test);
+        test.test_stat = 0;
+        test.p_value = 1.0;
+        //test.differential = 0.0;
+		
+		test.test_status = LOWDATA;
+    }
+    else // at least one is OK, the other might be LOW_DATA
 	{
 		test.test_status = FAIL;
 
@@ -137,14 +355,8 @@ pair<int, SampleDiffs::iterator>  get_de_tests(const string& description,
 			test.test_status = NOTEST;
 		
 	}
-	else
-	{
-		test.test_stat = 0;
-		test.test_stat = 1.0;
-		test.differential = 0.0;
-		test.test_status = FAIL;
-	}
 	
+    
 	inserted.first->second = test;
 	
 	return make_pair(total_iso_de_tests, inserted.first);
@@ -165,10 +377,15 @@ void get_ds_tests(const AbundanceGroup& prev_abundance,
 	inserted = diff_tests.insert(make_pair(name,SampleDifference())); 
 	SampleDifference test;
 	
-	test.gene_names = curr_abundance.gene_name();
-	test.protein_ids = curr_abundance.protein_id();
-	test.locus_desc = curr_abundance.locus_tag();
-	test.description = curr_abundance.description();
+    shared_ptr<SampleDifferenceMetaData> meta_data = get_metadata(name);
+    
+    meta_data->gene_ids = curr_abundance.gene_id();
+    meta_data->gene_names = curr_abundance.gene_name();
+    meta_data->protein_ids = curr_abundance.protein_id();
+    meta_data->locus_desc = curr_abundance.locus_tag();
+    meta_data->description = curr_abundance.description();
+    
+    test.meta_data = meta_data;
 	
 	test.test_status = NOTEST;
 	
@@ -248,11 +465,14 @@ void get_ds_tests(const AbundanceGroup& prev_abundance,
 			}
 			else
 			{
+                // We're dealing with a standard normal that's been truncated below zero
+                // so pdf(js) is twice the standard normal, and cdf is 0.5 * (cdf of normal - 1)
+                
 				normal test_dist(0,1.0);
 				//double denom = sqrt(js_var);
-				test.test_stat = js;
 				double p = js/sqrt(js_var);
-				test.p_value = 1.0 - cdf(test_dist, p);
+                test.test_stat = 2 * pdf(test_dist, p);
+				test.p_value = 1.0 - ((cdf(test_dist, p) - 0.5) / 0.5);
 				test.value_1 = 0;
 				test.value_2 = 0;
 				test.differential = js;
@@ -267,9 +487,15 @@ void get_ds_tests(const AbundanceGroup& prev_abundance,
 
 		inserted.first->second = test;
 	}
-	else
+	else // we won't even bother with the JS-based testing in LOWDATA cases.
 	{
-		test.test_status = FAIL;
+        if (prev_status == NUMERIC_OK && curr_status == NUMERIC_OK)
+            test.test_status = NOTEST;
+        else if (prev_status == NUMERIC_FAIL || curr_status == NUMERIC_FAIL)
+            test.test_status = FAIL;
+        else
+            test.test_status = LOWDATA;
+            
 		test.test_stat = 0;
 		test.p_value = 0.0;
 		test.differential = 0.0;
@@ -301,10 +527,12 @@ void add_to_tracking_table(size_t sample_index,
 	FPKMTracking& fpkm_track = inserted.first->second;
 	
 	set<string> tss = ab.tss_id();
+    set<string> gene_ids = ab.gene_id();
 	set<string> genes = ab.gene_name();
 	set<string> proteins = ab.protein_id();
 	
 	fpkm_track.tss_ids.insert(tss.begin(), tss.end());
+    fpkm_track.gene_ids.insert(gene_ids.begin(), gene_ids.end());
 	fpkm_track.gene_names.insert(genes.begin(), genes.end());
 	fpkm_track.protein_ids.insert(proteins.begin(), proteins.end());
 	
@@ -323,6 +551,14 @@ void add_to_tracking_table(size_t sample_index,
 			fpkm_track.classcode = 0;
 			fpkm_track.ref_match = "-";
 		}
+        if (transfrag)
+        {
+            fpkm_track.length = transfrag->length(); 
+        }
+        else
+        {
+            fpkm_track.length = 0;
+        }
 	}
 	
 	FPKMContext r1 = FPKMContext(ab.num_fragments(), 
@@ -424,14 +660,15 @@ void sample_abundance_worker(const string& locus_tag,
     vector<AbundanceGroup> transcripts_by_gene_id;
     cluster_transcripts<ConnectByAnnotatedGeneId>(sample.transcripts,
                                                   transcripts_by_gene_id);
-    foreach(AbundanceGroup& ab_group, transcripts_by_gene_id)
+    
+	foreach(AbundanceGroup& ab_group, transcripts_by_gene_id)
     {
         ab_group.locus_tag(locus_tag);
         set<string> gene_ids = ab_group.gene_id();
         assert (gene_ids.size() == 1);
         ab_group.description(*(gene_ids.begin()));
     }
-    
+	
     sample.genes = transcripts_by_gene_id;
     
     if (perform_cds_analysis)
@@ -456,12 +693,15 @@ void sample_abundance_worker(const string& locus_tag,
         
         // Group the CDS clusters by gene
         vector<shared_ptr<Abundance> > cds_abundances;
+        double max_cds_mass_variance = 0.0; 
         foreach (AbundanceGroup& ab_group, sample.cds)
         {
             cds_abundances.push_back(shared_ptr<Abundance>(new AbundanceGroup(ab_group)));
+            max_cds_mass_variance = max(ab_group.max_mass_variance(), max_cds_mass_variance);
         }
         AbundanceGroup cds(cds_abundances,
-                           cds_gamma_cov);
+                           cds_gamma_cov,
+                           max_cds_mass_variance);
         
         vector<AbundanceGroup> cds_by_gene;
         
@@ -489,6 +729,7 @@ void sample_abundance_worker(const string& locus_tag,
                                                      transcripts_by_tss,
                                                      &tss_gamma_cov);
         
+       
         foreach(AbundanceGroup& ab_group, transcripts_by_tss)
         {
             ab_group.locus_tag(locus_tag);
@@ -497,19 +738,24 @@ void sample_abundance_worker(const string& locus_tag,
             string desc = *(tss_ids.begin()); 
             assert (desc != "");
             ab_group.description(*(tss_ids.begin()));
+            
         }
         
         sample.primary_transcripts = transcripts_by_tss;
+        double max_tss_mass_variance = 0.0;
         
         // Group TSS clusters by gene
         vector<shared_ptr<Abundance> > primary_transcript_abundances;
         foreach (AbundanceGroup& ab_group, sample.primary_transcripts)
         {
             primary_transcript_abundances.push_back(shared_ptr<Abundance>(new AbundanceGroup(ab_group)));
+            max_tss_mass_variance = max(max_tss_mass_variance, ab_group.max_mass_variance());
         }
         
         AbundanceGroup primary_transcripts(primary_transcript_abundances,
-                                           tss_gamma_cov);
+                                           tss_gamma_cov,
+                                           
+                                           max_tss_mass_variance);
         
         vector<AbundanceGroup> primary_transcripts_by_gene;
         
@@ -531,19 +777,28 @@ void sample_abundance_worker(const string& locus_tag,
 void sample_worker(const RefSequenceTable& rt,
                    ReplicatedBundleFactory& sample_factory,
                    shared_ptr<SampleAbundances> abundance,
-                   shared_ptr<bool> non_empty)
+                   size_t factory_id,
+                   shared_ptr<TestLauncher> launcher)
 {
 #if ENABLE_THREADS
 	boost::this_thread::at_thread_exit(decr_pool_count);
 #endif
     
     HitBundle bundle;
-    *non_empty = sample_factory.next_bundle(bundle);
+    bool non_empty = sample_factory.next_bundle(bundle);
     
-    if (!*non_empty)
-        return;
-    if (!final_est_run && bundle.ref_scaffolds().size() != 1) // Only learn on single isoforms
+    if (!non_empty || (!corr_multi && !final_est_run && bundle.ref_scaffolds().size() != 1)) // Only learn on single isoforms
+    {
+#if !ENABLE_THREADS
+        // If Cuffdiff was built without threads, we need to manually invoke 
+        // the testing functor, which will check to see if all the workers
+        // are done, and if so, perform the cross sample testing.
+        launcher->abundance_avail(locus_tag, abundance, factory_id);
+        launcher->test_finished_loci();
+        //launcher();
+#endif
     	return;
+    }
     
     abundance->cluster_mass = bundle.mass();
     
@@ -554,6 +809,9 @@ void sample_worker(const RefSequenceTable& rt,
             bundle.left(),
             bundle.right());
     string locus_tag = bundle_label_buf;
+    
+    launcher->register_locus(locus_tag);
+    
     abundance->locus_tag = locus_tag;
     
     bool perform_cds_analysis = final_est_run;
@@ -581,8 +839,19 @@ void sample_worker(const RefSequenceTable& rt,
     {
         ref_scaff->clear_hits();
     }
+    
+    launcher->abundance_avail(locus_tag, abundance, factory_id);
+    launcher->test_finished_loci();
+    
+#if !ENABLE_THREADS
+    // If Cuffdiff was built without threads, we need to manually invoke 
+    // the testing functor, which will check to see if all the workers
+    // are done, and if so, perform the cross sample testing.
+    //launcher->test_finished_loci();
+#endif
 }
 
+int total_tests = 0;
 void test_differential(const string& locus_tag,
 					   const vector<shared_ptr<SampleAbundances> >& samples,
 					   Tests& tests,
@@ -594,8 +863,11 @@ void test_differential(const string& locus_tag,
     
 #if ENABLE_THREADS
 	test_storage_lock.lock();
+    total_tests++;
 #endif
     
+    //fprintf(stderr, "\nTesting in %s (%d total tests)\n", locus_tag.c_str(), total_tests);
+    
 	// Add all the transcripts, CDS groups, TSS groups, and genes to their
     // respective FPKM tracking table.  Whether this is a time series or an
     // all pairs comparison, we should be calculating and reporting FPKMs for 
@@ -629,7 +901,7 @@ void test_differential(const string& locus_tag,
     // by the user.
 	for (size_t i = 1; i < samples.size(); ++i)
 	{
-		bool multi_transcript_locus = samples[i]->transcripts.abundances().size() > 1;
+		//bool multi_transcript_locus = samples[i]->transcripts.abundances().size() > 1;
 		
         int sample_to_start_test_against = 0;
         if (samples_are_time_series)
@@ -655,10 +927,14 @@ void test_differential(const string& locus_tag,
                                       tests.isoform_de_tests[i][j],
                                       enough_reads);
                 
-                result.second->second.gene_names = curr_abundance.gene_name();
-                result.second->second.protein_ids = curr_abundance.protein_id();
-                result.second->second.locus_desc = curr_abundance.locus_tag();
-                result.second->second.description = curr_abundance.description();
+                shared_ptr<SampleDifferenceMetaData> meta_data = get_metadata(desc);
+                
+                meta_data->gene_ids = curr_abundance.gene_id();
+                meta_data->gene_names = curr_abundance.gene_name();
+                meta_data->protein_ids = curr_abundance.protein_id();
+                meta_data->locus_desc = curr_abundance.locus_tag();
+                meta_data->description = curr_abundance.description();
+                result.second->second.meta_data = meta_data;
             }
             
             for (size_t k = 0; k < samples[i]->cds.size(); ++k)
@@ -675,10 +951,14 @@ void test_differential(const string& locus_tag,
                              tests.cds_de_tests[i][j],
                              enough_reads);
                 
-                result.second->second.gene_names = curr_abundance.gene_name();
-                result.second->second.protein_ids = curr_abundance.protein_id();
-                result.second->second.locus_desc = curr_abundance.locus_tag();
-                result.second->second.description = curr_abundance.description();
+                shared_ptr<SampleDifferenceMetaData> meta_data = get_metadata(desc);
+                
+                meta_data->gene_ids = curr_abundance.gene_id();
+                meta_data->gene_names = curr_abundance.gene_name();
+                meta_data->protein_ids = curr_abundance.protein_id();
+                meta_data->locus_desc = curr_abundance.locus_tag();
+                meta_data->description = curr_abundance.description();
+                result.second->second.meta_data = meta_data;
             }
             
             for (size_t k = 0; k < samples[i]->primary_transcripts.size(); ++k)
@@ -695,10 +975,14 @@ void test_differential(const string& locus_tag,
                              tests.tss_group_de_tests[i][j],
                              enough_reads);
                 
-                result.second->second.gene_names = curr_abundance.gene_name();
-                result.second->second.protein_ids = curr_abundance.protein_id();
-                result.second->second.locus_desc = curr_abundance.locus_tag();
-                result.second->second.description = curr_abundance.description();
+                shared_ptr<SampleDifferenceMetaData> meta_data = get_metadata(desc);
+                
+                meta_data->gene_ids = curr_abundance.gene_id();
+                meta_data->gene_names = curr_abundance.gene_name();
+                meta_data->protein_ids = curr_abundance.protein_id();
+                meta_data->locus_desc = curr_abundance.locus_tag();
+                meta_data->description = curr_abundance.description();
+                result.second->second.meta_data = meta_data;
             }
             
             for (size_t k = 0; k < samples[i]->genes.size(); ++k)
@@ -715,10 +999,14 @@ void test_differential(const string& locus_tag,
                              tests.gene_de_tests[i][j],
                              enough_reads);
                 
-                result.second->second.gene_names = curr_abundance.gene_name();
-                result.second->second.protein_ids = curr_abundance.protein_id();
-                result.second->second.locus_desc = curr_abundance.locus_tag();
-                result.second->second.description = curr_abundance.description();
+                shared_ptr<SampleDifferenceMetaData> meta_data = get_metadata(desc);
+                
+                meta_data->gene_ids = curr_abundance.gene_id();
+                meta_data->gene_names = curr_abundance.gene_name();
+                meta_data->protein_ids = curr_abundance.protein_id();
+                meta_data->locus_desc = curr_abundance.locus_tag();
+                meta_data->description = curr_abundance.description();
+                result.second->second.meta_data = meta_data;
             }
             
             // FIXME: the code below will not properly test for differential
diff --git a/src/differential.h b/src/differential.h
index 91514f4..26fa2bd 100644
--- a/src/differential.h
+++ b/src/differential.h
@@ -16,6 +16,7 @@
 #include <cstdlib>
 #include <set>
 #include <map>
+#include <utility>
 #include <vector>
 #include <string>
 
@@ -25,15 +26,26 @@
 
 #include "abundances.h"
 #include "jensen_shannon.h"
+#include "replicates.h"
 
 using namespace std;
 
 enum TestStatus {
 	NOTEST,  // successful calculation, test not performed
+    LOWDATA, // unsuccessful calculation due to low data, test not performed
 	OK,      // successful numerical calc, test performed
 	FAIL     // numerical exception, test not performed
 }; 
 
+struct SampleDifferenceMetaData
+{
+    string locus_desc;
+    set<string> gene_ids;
+	set<string> gene_names;
+	set<string> protein_ids;
+	string description; // isoforms or tss groups (e.g.) involved in this test
+};
+
 // Stores the differential expression of an isoform or set of isoforms in two
 // different samples, along with a significance test statistic for the difference.
 struct SampleDifference
@@ -45,6 +57,7 @@ struct SampleDifference
 	value_2(0.0),
 	test_stat(0.0),
     p_value(1.0),
+	corrected_p(1.0),
 	tested_group_id(-1),
 	test_status(NOTEST),
 	significant(false){}
@@ -57,19 +70,18 @@ struct SampleDifference
 	double differential;
 	double test_stat;
 	double p_value;
+	double corrected_p;
 	
 	size_t tested_group_id; // which scaffolds' FPKMs contribute
 	
-	string locus_desc;
-	set<string> gene_names;
-	set<string> protein_ids;
-	string description; // isoforms or tss groups (e.g.) involved in this test
-	
+    shared_ptr<SampleDifferenceMetaData> meta_data;
+
 	TestStatus test_status;
 	bool significant;
 };
 
 typedef map<string, SampleDifference > SampleDiffs;
+typedef map<string, shared_ptr<SampleDifferenceMetaData> > SampleDiffMetaDataTable;
 
 struct Outfiles
 {
@@ -115,10 +127,12 @@ struct FPKMTracking
 	string locus_tag;
 	char classcode;
 	set<string> tss_ids; // for individual isoforms only
+    set<string> gene_ids;
 	set<string> gene_names;
 	set<string> protein_ids;
 	string description; // isoforms or tss groups (e.g.) involved in this test
 	string ref_match;
+    int length;
 	
 	TestStatus test_status;
 	
@@ -135,128 +149,6 @@ struct Tracking
 	FPKMTrackingTable cds_fpkm_tracking;
 };
 
-// This factory merges bundles in a requested locus from several replicates
-class ReplicatedBundleFactory
-{
-public:
-	ReplicatedBundleFactory(const vector<shared_ptr<BundleFactory> >& factories)
-    : _factories(factories) {}
-	
-	int num_bundles() { return _factories[0]->num_bundles(); }
-	vector<shared_ptr<BundleFactory> > factories() { return _factories; }
-	
-	bool next_bundle(HitBundle& bundle_out)
-    {
-        vector<HitBundle*> bundles;
-        
-        bool non_empty_bundle = false;
-        foreach (shared_ptr<BundleFactory> fac, _factories)
-        {
-            bundles.push_back(new HitBundle());
-            if (fac->next_bundle(*(bundles.back())))
-            {
-                non_empty_bundle = true;
-            }
-        }
-        
-        if (non_empty_bundle == false)
-        {
-            foreach (HitBundle* in_bundle, bundles)
-            {
-                in_bundle->ref_scaffolds().clear();
-                in_bundle->clear_hits();
-                delete in_bundle;
-            }
-            return false;
-        }
-        
-        for (size_t i = 1; i < bundles.size(); ++i)
-        {
-            const vector<shared_ptr<Scaffold> >& s1 = bundles[i]->ref_scaffolds();
-            const vector<shared_ptr<Scaffold> >& s2 =  bundles[i-1]->ref_scaffolds();
-            assert (s1.size() == s2.size());
-            for (size_t j = 0; j < s1.size(); ++j)
-            {
-                assert (s1[j]->annotated_trans_id() == s2[j]->annotated_trans_id());
-            }
-        }
-        
-        // Merge the replicates into a combined bundle of hits.
-        HitBundle::combine(bundles, bundle_out);
-        
-        foreach (HitBundle* in_bundle, bundles)
-        {
-            in_bundle->ref_scaffolds().clear();
-            in_bundle->clear_hits();
-            delete in_bundle;
-        }
-        return true;
-    }
-	
-	void reset() 
-    {
-        foreach (shared_ptr<BundleFactory> fac, _factories)
-        {
-            fac->reset();
-        }
-    }
-    
-    void inspect_replicate_maps(int& min_len, int& max_len)
-    {
-        foreach (shared_ptr<BundleFactory> fac, _factories)
-        {
-//            shared_ptr<ReadGroupProperties> rg_props(new ReadGroupProperties);
-//            if (global_read_properties)
-//            {
-//                *rg_props = *global_read_properties;
-//            }
-//            else 
-//            {
-//                *rg_props = *fac->read_group_properties();
-//            }
-
-            long double map_mass = 0.0;
-            BadIntronTable bad_introns;
-            
-            shared_ptr<EmpDist> frag_len_dist(new EmpDist);
-            
-            inspect_map(*fac, map_mass, NULL, *frag_len_dist, false);
-            
-            shared_ptr<ReadGroupProperties> rg_props = fac->read_group_properties();
-            rg_props->frag_len_dist(frag_len_dist);
-            rg_props->total_map_mass(map_mass);
-            fac->read_group_properties(rg_props);
-            
-			min_len = min(min_len, frag_len_dist->min());
-			max_len = max(max_len, frag_len_dist->max());
-        }
-		
-    }
-	
-    
-    // This function NEEDS to deep copy the ref_mRNAs, otherwise cuffdiff'd
-    // samples will clobber each other
-    void set_ref_rnas(const vector<shared_ptr<Scaffold> >& mRNAs)
-    {
-        foreach(shared_ptr<BundleFactory> fac, _factories)
-        {
-            fac->set_ref_rnas(mRNAs);
-        }
-    }
-    
-    void set_mask_rnas(const vector<shared_ptr<Scaffold> >& mRNAs)
-    {
-        foreach(shared_ptr<BundleFactory> fac, _factories)
-        {
-            fac->set_mask_rnas(mRNAs);
-        }
-    }
-    
-private:
-	vector<shared_ptr<BundleFactory> > _factories;
-};
-
-
 struct SampleAbundances
 {
     string locus_tag;
@@ -269,12 +161,63 @@ struct SampleAbundances
 	double cluster_mass;
 };
 
+#if ENABLE_THREADS
+    extern boost::mutex _launcher_lock;
+#endif
+
+struct TestLauncher
+{
+private:
+    TestLauncher(TestLauncher& rhs) {}
+    
+public:
+    TestLauncher(int num_samples,
+                 Tests* tests,
+                 Tracking* tracking,
+                 bool ts,
+                 ProgressBar* p_bar) 
+    :
+    _orig_workers(num_samples),
+    _tests(tests),
+    _tracking(tracking),
+    _samples_are_time_series(ts),
+    _p_bar(p_bar)
+    {
+    }
+    
+    void operator()();
+    
+    void register_locus(const string& locus_id);
+    void abundance_avail(const string& locus_id, 
+                         shared_ptr<SampleAbundances> ab, 
+                         size_t factory_id);
+    void test_finished_loci();
+    void perform_testing(vector<shared_ptr<SampleAbundances> >& abundances);
+    bool all_samples_reported_in(vector<shared_ptr<SampleAbundances> >& abundances);
+    bool all_samples_reported_in(const string& locus_id);
+    
+    typedef list<pair<string, vector<shared_ptr<SampleAbundances> > > > launcher_sample_table;
+    
+private:
+    
+    launcher_sample_table::iterator find_locus(const string& locus_id);
+    
+    int _orig_workers;
+    launcher_sample_table _samples;
+    Tests* _tests;
+    Tracking* _tracking;
+    bool _samples_are_time_series;
+    ProgressBar* _p_bar;
+
+};
+
 extern double min_read_count;
 
 void sample_worker(const RefSequenceTable& rt,
                    ReplicatedBundleFactory& sample_factory,
                    shared_ptr<SampleAbundances> abundance,
-                   shared_ptr<bool> non_empty);
+                   size_t factory_id,
+                   shared_ptr<TestLauncher> launcher);
 
 void test_differential(const string& locus_tag,
 					   const vector<shared_ptr<SampleAbundances> >& samples,
@@ -284,6 +227,9 @@ void test_differential(const string& locus_tag,
 
 #if ENABLE_THREADS
 void decr_pool_count();
+extern boost::mutex locus_thread_pool_lock;
+extern int locus_curr_threads;
+extern int locus_num_threads;
 #endif
 
 #endif
diff --git a/src/filters.cpp b/src/filters.cpp
index fc364e4..f426ed6 100644
--- a/src/filters.cpp
+++ b/src/filters.cpp
@@ -10,8 +10,14 @@
 #include "filters.h"
 #include <algorithm>
 #include <numeric>
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/depth_first_search.hpp>
+#include <boost/graph/visitors.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/connected_components.hpp>
 
 using namespace std;
+using namespace boost;
 
 void filter_introns(int bundle_length,
 					int bundle_left,
@@ -38,11 +44,11 @@ void filter_introns(int bundle_length,
 	{
 		bundle_avg_doc = major_isoform_intron_doc(intron_doc);
 		bundle_avg_thresh = fraction * bundle_avg_doc;
-		asm_verbose("\tFiltering bundle introns, avg (intron) doc = %lf, thresh = %f\n", bundle_avg_doc, bundle_avg_thresh);
+		verbose_msg("\tFiltering bundle introns, avg (intron) doc = %lf, thresh = %f\n", bundle_avg_doc, bundle_avg_thresh);
 	}
 	else
 	{
-		asm_verbose("\tFiltering bundle introns, avg bundle doc = %lf, thresh = %f\n", bundle_avg_doc, bundle_avg_thresh);
+		verbose_msg("\tFiltering bundle introns, avg bundle doc = %lf, thresh = %f\n", bundle_avg_doc, bundle_avg_thresh);
 	}
 	
 	for(map<pair<int, int>, int>::const_iterator itr = intron_doc.begin();
@@ -65,7 +71,7 @@ void filter_introns(int bundle_length,
 					if (doc < bundle_avg_thresh)
 					{
 						toss[j] = true;
-						asm_verbose("\t Filtering intron %d - %d: %f thresh %f\n", itr->first.first, itr->first.second, doc, bundle_avg_thresh);
+						verbose_msg("\t Filtering intron %d - %d: %f thresh %f\n", itr->first.first, itr->first.second, doc, bundle_avg_thresh);
 						continue; 
 					}
 					
@@ -86,7 +92,7 @@ void filter_introns(int bundle_length,
 						double thresh = itr2->second * fraction;
 						if (doc < thresh)
 						{
-							asm_verbose("\t Filtering intron (due to overlap) %d - %d: %f thresh %f\n", itr->first.first, itr->first.second, doc, bundle_avg_thresh);
+							verbose_msg("\t Filtering intron (due to overlap) %d - %d: %f thresh %f\n", itr->first.first, itr->first.second, doc, bundle_avg_thresh);
 							toss[j] = true;
 						}
 					}
@@ -100,7 +106,7 @@ void filter_introns(int bundle_length,
 		if (!toss[j])
 		{
 			filtered_hits.push_back(hits[j]);
-//#if ASM_VERBOSE
+//#if verbose_msg
 //			if (hits[j].has_intron())
 //			{
 //				fprintf(stderr, "KEEPING intron scaff [%d-%d]\n", hits[j].left(), hits[j].right());
@@ -112,13 +118,13 @@ void filter_introns(int bundle_length,
 			if (hits[j].has_intron())
 			{
 				
-				asm_verbose("\tFiltering intron scaff [%d-%d]\n", hits[j].left(), hits[j].right());
+				verbose_msg("\tFiltering intron scaff [%d-%d]\n", hits[j].left(), hits[j].right());
 			}
 		}
 	}
 	
 	
-	asm_verbose("\tIntron filtering pass finished: excluded %d fragments\n", (int)hits.size() - (int)filtered_hits.size());
+	verbose_msg("\tIntron filtering pass finished: excluded %d fragments\n", (int)hits.size() - (int)filtered_hits.size());
 	hits = filtered_hits;
 }
 
@@ -189,13 +195,13 @@ void pre_mrna_filter(int bundle_length,
                                                    i_right - bundle_left);
         
         double cumul_cov = 0;
-        for (size_t i = 0; i < i_right - i_left; ++i)
+        for (int i = 0; i < i_right - i_left; ++i)
         {
             size_t pos = (i_left - bundle_left) + i;
             cumul_cov += depth_of_coverage[pos];
         }
         cumul_cov /= i_right - i_left;
-        asm_verbose("retained intron %d-%d background: %lf\n", i_left, i_right, intron_background);
+        verbose_msg("retained intron %d-%d background: %lf\n", i_left, i_right, intron_background);
         if (cumul_cov / bundle_avg_doc >= pre_mrna_fraction)
         {
             //fprintf(stderr, "\tskipping\n");
@@ -203,12 +209,14 @@ void pre_mrna_filter(int bundle_length,
             continue;
         }
         
+		//double thresh = (1.0/pre_mrna_fraction) * intron_background;
+		double thresh = pre_mrna_fraction * intron_background;
+
         for (size_t j = 0; j < hits.size(); ++j)
         {
-            //if (hits[j].has_intron())
-            //    continue;
-            double thresh = (1.0/pre_mrna_fraction) * intron_background;
-            
+            if (hits[j].is_ref())
+                continue;
+			
             int len = 0;
             double doc = 0.0;
             size_t curr_op = 0;
@@ -289,166 +297,22 @@ void pre_mrna_filter(int bundle_length,
 		if (!toss[j])
 		{
 			filtered_hits.push_back(hits[j]);
-			//#if ASM_VERBOSE
-			//			if (hits[j].has_intron())
-			//			{
-			//				
-			//				fprintf(stderr, "KEEPING intron scaff [%d-%d]\n", hits[j].left(), hits[j].right());
-			//			}
-			//#endif	
 		}
 		else
 		{
 			if (hits[j].has_intron())
 			{
 				
-                asm_verbose( "\t@@@ Filtering intron scaff [%d-%d]\n", hits[j].left(), hits[j].right());
+                verbose_msg( "\t@@@ Filtering intron scaff [%d-%d]\n", hits[j].left(), hits[j].right());
 			}
 		}
 	}
     
-	asm_verbose("\tPre-mRNA filter pass complete, excluded %lu fragments\n", hits.size() - filtered_hits.size());
+	verbose_msg("\tPre-mRNA filter pass complete, excluded %lu fragments\n", hits.size() - filtered_hits.size());
 	
 	hits = filtered_hits;
 }
 
-//void pre_mrna_filter(int bundle_length,
-//					 int bundle_left,
-//					 vector<Scaffold>& hits)
-//{
-//	vector<int> depth_of_coverage(bundle_length,0);
-//	vector<double> scaff_doc;
-//	map<pair<int,int>, int> intron_doc;
-//	vector<Scaffold> filtered_hits;
-//	vector<bool> toss(hits.size(), false);
-//	
-//	// Make sure the avg only uses stuff we're sure isn't pre-mrna fragments
-//	double bundle_avg_doc = compute_doc(bundle_left, 
-//										hits, 
-//										depth_of_coverage, 
-//										intron_doc,
-//										true);
-//	
-//	// recompute the real DoCs
-//	compute_doc(bundle_left, 
-//				hits, 
-//				depth_of_coverage, 
-//				intron_doc,
-//				false);
-//	
-//	record_doc_for_scaffolds(bundle_left, 
-//							 hits, 
-//							 depth_of_coverage, 
-//							 intron_doc,
-//							 scaff_doc);
-//	
-////	vector<int>::iterator new_end = remove(depth_of_coverage.begin(), depth_of_coverage.end(), 0);
-////	depth_of_coverage.erase(new_end, depth_of_coverage.end());
-////	sort(depth_of_coverage.begin(), depth_of_coverage.end());
-////	
-////	size_t median = floor(depth_of_coverage.size() / 2);
-//	
-//	
-//	Scaffold smashed_gene;
-//	vector<Scaffold> spliced_hits;
-//    for (size_t i = 0; i < hits.size(); ++i)
-//    {
-//        const vector<const MateHit*>& m_hits = hits[i].mate_hits();
-//        for (size_t j = 0; j < m_hits.size(); ++j)
-//        {
-//            if (m_hits[j]->left_alignment() && !m_hits[j]->left_alignment()->contiguous())
-//            {
-//                spliced_hits.push_back(Scaffold(MateHit(m_hits[j]->ref_id(),
-//                                                        m_hits[j]->left_alignment(),
-//                                                        shared_ptr<const ReadHit>(),
-//                                                        0,
-//                                                        0)));
-//
-//            }
-//            if (m_hits[j]->right_alignment() && !m_hits[j]->right_alignment()->contiguous())
-//            {
-//                spliced_hits.push_back(Scaffold(MateHit(m_hits[j]->ref_id(),
-//                                                        m_hits[j]->right_alignment(),
-//                                                        shared_ptr<const ReadHit>(),
-//                                                        0,
-//                                                        0)));
-//                
-//            }
-//        }
-//    }
-//    
-//	Scaffold::merge(spliced_hits, smashed_gene, false);
-//	vector<bool> constitutive_introns(intron_doc.size(), true);
-//	
-//    vector<pair<int, int> > gaps = smashed_gene.gaps();
-//    
-//	//size_t intron_idx = 0;
-//    
-//	for(map<pair<int, int>, int >::const_iterator itr = intron_doc.begin();
-//		itr != intron_doc.end(); 
-//		++itr)
-//	{
-//		int i_left = itr->first.first;
-//		int i_right = itr->first.second;
-//		
-//        double cumul_cov = 0;
-//        for (size_t i = 0; i < i_right - i_left; ++i)
-//        {
-//            size_t pos = (i_left - bundle_left) + i;
-//            cumul_cov += depth_of_coverage[pos];
-//        }
-//        cumul_cov /= i_right - i_left;
-//#if ASM_VERBOSE
-//        fprintf(stderr, "retained intron %d-%d depth of cov: %lf\n", i_left, i_right, cumul_cov);
-//#endif
-//        if (cumul_cov / bundle_avg_doc >= pre_mrna_fraction)
-//        {
-//            continue;
-//        }
-//        
-//        for (size_t j = 0; j < hits.size(); ++j)
-//        {
-//            //if (hits[j].has_intron())
-//            //    continue;
-//            if (hits[j].match_length(i_left, i_right))
-//            {
-//                toss[j] = true;
-//            }
-//		}
-//	}
-//    
-//	for (size_t j = 0; j < hits.size(); ++j)
-//	{	
-//		if (!toss[j])
-//		{
-//			filtered_hits.push_back(hits[j]);
-//			//#if ASM_VERBOSE
-//			//			if (hits[j].has_intron())
-//			//			{
-//			//				
-//			//				fprintf(stderr, "KEEPING intron scaff [%d-%d]\n", hits[j].left(), hits[j].right());
-//			//			}
-//			//#endif	
-//		}
-//		else
-//		{
-//#if ASM_VERBOSE
-//			//			if (hits[j].has_intron())
-//			//			{
-//			//				
-//			//				fprintf(stderr, "\tFiltering intron scaff [%d-%d]\n", hits[j].left(), hits[j].right());
-//			//			}
-//#endif	
-//		}
-//	}
-//	
-//	//#if ASM_VERBOSE
-//	//	fprintf(stderr, "\tInitial filter pass complete\n");
-//	//#endif
-//	
-//	hits = filtered_hits;
-//}
-
 void filter_hits(int bundle_length,
 				 int bundle_left,
 				 vector<Scaffold>& hits)
@@ -456,7 +320,7 @@ void filter_hits(int bundle_length,
 	
 	pre_mrna_filter(bundle_length, bundle_left, hits);
 	
-	vector<int> depth_of_coverage(bundle_length,0);
+	vector<int> depth_of_coverage(bundle_length+1,0);
 	vector<double> scaff_doc;
 	map<pair<int,int>, int> intron_doc;
 	vector<Scaffold> filtered_hits;
@@ -501,6 +365,10 @@ void filter_hits(int bundle_length,
 		{
 			for (size_t j = 0; j < hits.size(); ++j)
 			{
+				if (hits[j].is_ref())
+                {
+					continue;
+                }
 				int i_left = itr->first.first;
 				int i_right = itr->first.second;
 				int j_match_len = hits[j].match_length(i_left, i_right); 
@@ -587,7 +455,7 @@ void filter_hits(int bundle_length,
 		if (!toss[j])
 		{
 			filtered_hits.push_back(hits[j]);
-//#if ASM_VERBOSE
+//#if verbose_msg
 //			if (hits[j].has_intron())
 //			{
 //				
@@ -600,12 +468,12 @@ void filter_hits(int bundle_length,
 			if (hits[j].has_intron())
 			{
 				
-				asm_verbose("\t!!!Filtering intron scaff [%d-%d]\n", hits[j].left(), hits[j].right());
+				verbose_msg("\t!!!Filtering intron scaff [%d-%d]\n", hits[j].left(), hits[j].right());
 			}
 		}
 	}
 	
-//#if ASM_VERBOSE
+//#if verbose_msg
 //	fprintf(stderr, "\tInitial filter pass complete\n");
 //#endif
 	
@@ -623,7 +491,7 @@ void filter_hits(int bundle_length,
 								 dummy,
 								 false);
 	
-//#if ASM_VERBOSE
+//#if verbose_msg
 //	fprintf(stderr, "\tUpdated avg bundle doc = %lf\n", bundle_avg_doc);
 //#endif
 	
@@ -635,7 +503,7 @@ void filter_hits(int bundle_length,
 	
 	
 	
-//#if ASM_VERBOSE
+//#if verbose_msg
 //    double bundle_thresh = pre_mrna_fraction * bundle_avg_doc;
 //	fprintf(stderr, "\tthreshold is = %lf\n", bundle_thresh);
 //#endif
@@ -664,7 +532,7 @@ void filter_hits(int bundle_length,
 		if (!toss[j])
 		{
 			filtered_hits.push_back(hits[j]);
-//#if ASM_VERBOSE
+//#if verbose_msg
 //			if (hits[j].has_intron())
 //			{
 //				
@@ -676,7 +544,7 @@ void filter_hits(int bundle_length,
 		{
 			if (hits[j].has_intron())
 			{
-				asm_verbose("\t***Filtering intron scaff [%d-%d]\n", hits[j].left(), hits[j].right());
+				verbose_msg("\t***Filtering intron scaff [%d-%d]\n", hits[j].left(), hits[j].right());
 			}
 		}
 	}
@@ -688,7 +556,9 @@ void filter_hits(int bundle_length,
 
 
 void filter_junk_isoforms(vector<shared_ptr<Abundance> >& transcripts,
-						  vector<double>& abundances)
+						  vector<double>& abundances,
+                          const vector<shared_ptr<Abundance> >& mapped_transcripts,
+                          double locus_mass)
 {
 	//	vector<double>::iterator max_ab = std::max_element(abundances.begin(),
 	//													   abundances.end());
@@ -716,14 +586,12 @@ void filter_junk_isoforms(vector<shared_ptr<Abundance> >& transcripts,
 	vector<bool> repeats(transcripts.size(), false); // too many low-quality hits
 	vector<bool> too_rare(transcripts.size(), false); // too rare to be reliably quantitated, could be error
 	
-	vector<bool> illegal_microexon(transcripts.size(), false); // initial or terminal exons are too short
-	
 	//cerr << "Chucked : ";
 	for (size_t t = 0; t < transcripts.size(); ++t)
 	{
 		shared_ptr<Scaffold> scaff = transcripts[t]->transfrag();
-
-		if (allow_junk_filtering)
+        
+		if (!(scaff->is_ref()) && allow_junk_filtering)
 		{
 			const vector<const MateHit*> hits = scaff->mate_hits();
 			
@@ -745,32 +613,45 @@ void filter_junk_isoforms(vector<shared_ptr<Abundance> >& transcripts,
 				}
 			}
 			
-			double low_qual_hits = 0.0;
-			static const double low_qual_err_prob = high_phred_err_prob; // hits with error_prob() above this are low quality;
-			static const double low_qual_thresh = 0.75; // hits with more than this fraction of low qual hits are repeats
-			for (vector<const MateHit*>::const_iterator itr = hits.begin();
-				 itr != hits.end();
-				 ++itr)
-			{
-				double e = (*itr)->error_prob();
-				if (e >= low_qual_err_prob)
-					low_qual_hits += 1.0;
-			}
-			double low_qual_frac = low_qual_hits / (double)hits.size();
-			if (low_qual_frac > low_qual_thresh)
-				repeats[t] = true;
+            if (library_type != "transfrags")
+            {
+                double low_qual_hits = 0.0;
+                static const double low_qual_err_prob = high_phred_err_prob; // hits with error_prob() above this are low quality;
+                static const double low_qual_thresh = 0.75; // hits with more than this fraction of low qual hits are repeats
+                for (vector<const MateHit*>::const_iterator itr = hits.begin();
+                     itr != hits.end();
+                     ++itr)
+                {
+                    double e = 1-(*itr)->mass();
+                    if (e >= low_qual_err_prob)
+                        low_qual_hits += 1.0;
+                }
+            
+                double low_qual_frac = low_qual_hits / (double)hits.size();
+                if (low_qual_frac > low_qual_thresh)
+                    repeats[t] = true;
+            }
+            if (scaff->strand() == CUFF_FWD &&
+                (abundances[t] / max_fwd_ab) < min_isoform_fraction)
+                too_rare[t] = true;
+            if ((scaff->strand() == CUFF_REV ||  scaff->strand() == CUFF_STRAND_UNKNOWN) &&
+                (abundances[t] / max_rev_ab) < min_isoform_fraction)
+                too_rare[t] = true;
+
+            const vector<double>* cond_probs = (mapped_transcripts[t]->cond_probs());
+            if (cond_probs)
+            {
+                assert (library_type != "transfrags");
+                double supporting_hits = abundances[t] * locus_mass;
+                if (supporting_hits < min_frags_per_transfrag)
+                    chaff[t] = true;
+            }
 		}
-		
-		if (scaff->strand() == CUFF_FWD &&
-			(abundances[t] / max_fwd_ab) < min_isoform_fraction)
-			too_rare[t] = true;
-		if ((scaff->strand() == CUFF_REV ||  scaff->strand() == CUFF_STRAND_UNKNOWN) &&
-			(abundances[t] / max_rev_ab) < min_isoform_fraction)
-			too_rare[t] = true;
-        
-        if ((scaff->mate_hits().size() < min_frags_per_transfrag))
-            chaff[t] = true;
-		
+        else // we should still filter things that are zero to improve robustness of MAP estimation
+        {
+            if (abundances[t] == 0.0)
+                too_rare[t] = true;
+        }
 	}
 	
 	vector<shared_ptr<Abundance> > non_junk_transcripts;
@@ -784,7 +665,7 @@ void filter_junk_isoforms(vector<shared_ptr<Abundance> >& transcripts,
 		}
         else
         {
-            asm_verbose( "Filtering isoform %d-%d\n", transcripts[t]->transfrag()->left(), transcripts[t]->transfrag()->right());
+            verbose_msg( "Filtering isoform %d-%d\n", transcripts[t]->transfrag()->left(), transcripts[t]->transfrag()->right());
         }
 	}
 	
@@ -808,10 +689,19 @@ void filter_junk_genes(vector<Gene>& genes)
 	for (size_t i = 0; i < genes.size(); ++i)
 	{
 		const Gene& g = genes[i];
+		
+		if(g.has_ref_trans())
+		{
+			good_genes.push_back(g);
+			continue;
+		}
+		
 		bool good_gene = true;
 		for (size_t j = 0; j < all_isoforms.size(); ++j)
 		{
 			vector<pair<int, int> > introns = all_isoforms[j].scaffold().gaps();
+            
+            //assert (!allow_junk_filtering || all_isoforms[j].scaffold().mate_hits().size() >= min_frags_per_transfrag);
 			for (size_t k = 0; k < introns.size(); ++k)
 			{
 				if (g.left() > introns[k].first && g.right() < introns[k].second &&
@@ -834,10 +724,424 @@ void filter_junk_genes(vector<Gene>& genes)
         }
         else
         {
-            asm_verbose("Filtering transfrags from gene %d-%d\n", g.left(), g.right());
+            verbose_msg("Filtering transfrags from gene %d-%d\n", g.left(), g.right());
         }
 	}
 	
 	genes = good_genes;
 	
 }
+
+void clip_by_3_prime_dropoff(vector<Scaffold>& scaffolds)
+{
+    vector<pair<double, Scaffold*> > three_prime_ends;
+    
+    if (library_type != "transfrags")
+    {
+        foreach (Scaffold& scaff, scaffolds)
+        {
+            if (!(scaff.strand() == CUFF_FWD || scaff.strand() == CUFF_REV))
+                continue;
+
+            int scaff_len = scaff.length();
+            vector<double> coverage(scaff_len, 0.0);
+            
+            double total = 0;
+            foreach(const MateHit* hit, scaff.mate_hits())
+            {
+                int start, end, frag_len;
+                if (!scaff.map_frag(*hit, start, end, frag_len)) continue;
+                
+                if (scaff.strand() == CUFF_REV)
+                {
+                    start = scaff_len - 1 - start;
+                    end = scaff_len - 1 - end;
+                    swap(start, end);
+                }
+                
+                for(int i = start; i <= end; ++i)
+                {
+                    coverage[i] += hit->mass();
+                    total += hit->mass();
+                }
+            }
+            double avg_cov = total/scaff_len;
+    //        if (avg_cov < trim_3_avgcov_thresh)
+    //            continue;
+            
+            const AugmentedCuffOp* exon_3 = NULL;
+            int mult;
+            int offset;
+            
+            if (scaff.strand() == CUFF_REV)
+            {
+                mult = 1;
+                offset = 0;
+                exon_3 = &scaff.augmented_ops().front();
+            }
+            else if (scaff.strand() == CUFF_FWD)
+            {
+                mult = -1;
+                offset = scaff_len - 1;
+                exon_3 = &scaff.augmented_ops().back();
+            }
+            else
+            {
+                continue;
+            }
+
+            int to_remove;
+            double min_cost = numeric_limits<double>::max();
+            double mean_to_keep = 0.0;
+            double mean_to_trim = 0.0;
+            double tmp_mean_to_trim = 0.0;
+            double tmp_mean_to_keep = 0.0;
+            double tmp_mean_3prime = 0.0;
+            for (int i = 0; i < exon_3->genomic_length; i++)
+            {
+                tmp_mean_3prime += coverage[offset + mult*i];
+            }
+            tmp_mean_3prime /= exon_3->genomic_length;
+            
+            double base_cost = 0.0;
+            for (int i = 0; i < exon_3->genomic_length; i++)
+            {
+                double d = (coverage[offset + mult*i] - tmp_mean_3prime);
+                d *= d;
+                base_cost += d;
+            }
+            base_cost /= exon_3->genomic_length;
+            
+            size_t min_cost_x = -1;
+            for (to_remove = 1; to_remove < exon_3->genomic_length - 1; to_remove++)
+            {
+                tmp_mean_to_trim = 0.0;
+                tmp_mean_to_keep = 0.0;
+                for (size_t i = 0; i < exon_3->genomic_length; i++)
+                {
+                    if (i <= to_remove)
+                    {
+                        tmp_mean_to_trim += coverage[offset + mult*i];
+                    }
+                    else 
+                    {
+                        tmp_mean_to_keep += coverage[offset + mult*i];
+                    }
+                }
+                
+                tmp_mean_to_trim /= to_remove;
+                tmp_mean_to_keep /= (exon_3->genomic_length - to_remove);
+                
+                double tmp_mean_trim_cost = 0.0;
+                double tmp_mean_keep_cost = 0.0;
+                for (int i = 0; i < exon_3->genomic_length; i++)
+                {
+                    if (i <= to_remove)
+                    {
+                        double d = (coverage[offset + mult*i] - tmp_mean_to_trim);
+                        d *= d;
+                        tmp_mean_trim_cost += d;
+                    }
+                    else 
+                    {
+                        double d = (coverage[offset + mult*i] - tmp_mean_to_keep);
+                        d *= d;
+                        tmp_mean_keep_cost += d;
+                    }
+                }
+                
+                tmp_mean_trim_cost /= to_remove;
+                tmp_mean_keep_cost /= (exon_3->genomic_length - to_remove);
+                
+                double new_cost = tmp_mean_trim_cost + tmp_mean_keep_cost;
+                
+                if (new_cost < min_cost && trim_3_dropoff_frac * tmp_mean_to_keep > tmp_mean_to_trim && new_cost < base_cost && to_remove > scaff_len * 0.05)
+                {
+                    min_cost = tmp_mean_trim_cost + tmp_mean_keep_cost;
+                    min_cost_x = to_remove;
+                    mean_to_keep = tmp_mean_to_keep;
+                    mean_to_trim = tmp_mean_to_trim;
+                }
+            }
+            
+            // If trimming reduces the overall mean squared error of the coverage
+            // do it
+            if (avg_cov >= trim_3_avgcov_thresh && min_cost_x < exon_3->genomic_length)
+            {
+                scaff.trim_3(min_cost_x);
+            }
+            
+            // store the mean squared error for this exon
+            tmp_mean_3prime = 0.0;
+            for (int i = 0; i < exon_3->genomic_length; i++)
+            {
+                tmp_mean_3prime += coverage[offset + mult*i];
+            }
+            tmp_mean_3prime /= exon_3->genomic_length;
+            
+            base_cost = 0.0;
+            for (int i = 0; i < exon_3->genomic_length; i++)
+            {
+                double d = (coverage[offset + mult*i] - tmp_mean_3prime);
+                d *= d;
+                base_cost += d;
+            }
+            base_cost /= exon_3->genomic_length;
+            three_prime_ends.push_back(make_pair(base_cost, &scaff));
+        }
+    }
+    else
+    {
+        foreach (Scaffold& scaff, scaffolds)
+        {
+            if (!(scaff.strand() == CUFF_FWD || scaff.strand() == CUFF_REV))
+                continue;
+            
+            int scaff_len = scaff.length();
+            vector<double> coverage(scaff_len, 0.0);
+            
+            double total = 0;
+            foreach(const MateHit* hit, scaff.mate_hits())
+            {
+                int start, end, frag_len;
+                if (!scaff.map_frag(*hit, start, end, frag_len)) continue;
+                
+                if (scaff.strand() == CUFF_REV)
+                {
+                    start = scaff_len - 1 - start;
+                    end = scaff_len - 1 - end;
+                    swap(start, end);
+                }
+                
+                for(int i = start; i <= end; ++i)
+                {
+                    coverage[i] += hit->mass();
+                    total += hit->mass();
+                }
+            }
+            double avg_cov = total/scaff_len;
+            //        if (avg_cov < trim_3_avgcov_thresh)
+            //            continue;
+            
+            const AugmentedCuffOp* exon_3 = NULL;
+            int mult;
+            int offset;
+            
+            if (scaff.strand() == CUFF_REV)
+            {
+                mult = 1;
+                offset = 0;
+                exon_3 = &scaff.augmented_ops().front();
+            }
+            else if (scaff.strand() == CUFF_FWD)
+            {
+                mult = -1;
+                offset = scaff_len - 1;
+                exon_3 = &scaff.augmented_ops().back();
+            }
+            else
+            {
+                continue;
+            }
+            
+            three_prime_ends.push_back(make_pair(scaff.fpkm(), &scaff));
+        }
+        
+    }
+    
+    adjacency_list <vecS, vecS, undirectedS> G;
+	
+	for (size_t i = 0; i < three_prime_ends.size(); ++i)
+	{
+		add_vertex(G);
+	}
+	
+	for (size_t i = 0; i < three_prime_ends.size(); ++i)
+	{
+		Scaffold* scaff_i = three_prime_ends[i].second;
+		//assert (scaff_i);
+		
+        const AugmentedCuffOp* scaff_i_exon_3 = NULL;
+        
+        if (scaff_i->strand() == CUFF_REV)
+        {
+            scaff_i_exon_3 = &(scaff_i->augmented_ops().front());
+        }
+        else if (scaff_i->strand() == CUFF_FWD)
+        {
+            scaff_i_exon_3 = &(scaff_i->augmented_ops().back());
+        }
+        
+		for (size_t j = i + 1; j < three_prime_ends.size(); ++j)
+		{
+			Scaffold* scaff_j = three_prime_ends[j].second;
+            
+            if (scaff_i->strand() != scaff_j->strand())
+                continue;
+            
+            const AugmentedCuffOp* scaff_j_exon_3 = NULL;
+            
+            if (scaff_j->strand() == CUFF_REV)
+            {
+                scaff_j_exon_3 = &(scaff_j->augmented_ops().front());
+            }
+            else if (scaff_j->strand() == CUFF_FWD)
+            {
+                scaff_j_exon_3 = &(scaff_j->augmented_ops().back());
+            }
+			
+			if (AugmentedCuffOp::overlap_in_genome(*scaff_j_exon_3, *scaff_i_exon_3) && 
+                AugmentedCuffOp::compatible(*scaff_j_exon_3, *scaff_i_exon_3, 0))
+				add_edge(i, j, G);
+		}
+	}
+	
+	std::vector<int> component(num_vertices(G));
+	connected_components(G, &component[0]);
+	
+	vector<vector<bool> > clusters(three_prime_ends.size(), 
+								   vector<bool>(three_prime_ends.size(), false));
+	
+	//vector<vector<size_t> > cluster_indices(three_prime_ends.size());
+    
+    vector<vector<pair<double, Scaffold*> > > grouped_scaffolds(three_prime_ends.size());
+	for (size_t i = 0; i < three_prime_ends.size(); ++i)
+	{
+		clusters[component[i]][i] = true;
+		grouped_scaffolds[component[i]].push_back(three_prime_ends[i]);
+	}
+    
+    for (size_t i = 0; i < grouped_scaffolds.size(); ++i)
+    {
+        vector<pair<double, Scaffold*> >& group = grouped_scaffolds[i];
+        sort(group.begin(), group.end());
+        if (group.empty())
+            continue;
+        
+        Scaffold* group_leader = NULL;
+        int trim_point = -1;
+        
+        const AugmentedCuffOp* group_exon_3 = NULL;
+        vector<pair<double, Scaffold*> >::iterator l_itr = group.begin();
+        while (l_itr != group.end())
+        {
+            Scaffold* possible_leader = l_itr->second;
+            bool ok_clip_leader = true;
+            vector<pair<double, Scaffold*> >::iterator g_itr = group.begin();
+            const AugmentedCuffOp* l_exon_3 = NULL;
+            CuffStrand s = possible_leader->strand();
+
+            if (s != CUFF_STRAND_UNKNOWN)
+            {  
+                if (s == CUFF_REV)
+                    l_exon_3 = &(possible_leader->augmented_ops().front());
+                else 
+                    l_exon_3 = &(possible_leader->augmented_ops().back());
+                for (; g_itr != group.end(); ++g_itr)
+                {
+                    const AugmentedCuffOp* g_exon_3 = NULL;
+                    if (s == CUFF_REV)
+                    {
+                        //  bad:
+                        //              leader  
+                        //    follower
+                        g_exon_3 = &(g_itr->second->augmented_ops().front());
+                        if (g_exon_3->g_right() <= l_exon_3->g_left())
+                            ok_clip_leader = false;
+                        
+                        // for meta-assembly libraries, don't ever allow clipping, just extension
+                        // bad:
+                        //             leader
+                        //         follower
+                        if (library_type == "transfrags" && 
+                            g_exon_3->g_left() < l_exon_3->g_left())
+                            ok_clip_leader = false;
+                    }
+                    else 
+                    {
+                        //  bad:
+                        //          follower  
+                        //  leader
+                        g_exon_3 = &(g_itr->second->augmented_ops().back());
+                        if (g_exon_3->g_left() >= l_exon_3->g_right())
+                            ok_clip_leader = false;
+                        
+                        // for meta-assembly libraries, don't ever allow clipping, just extension
+                        // bad:
+                        //             leader
+                        //                follower
+                        if (library_type == "transfrags" && 
+                            g_exon_3->g_right() > l_exon_3->g_right())
+                            ok_clip_leader = false;
+                    }
+                }
+            }
+            else
+            {
+                ok_clip_leader = false;
+            }
+            
+            if (ok_clip_leader)
+            {
+                if (s == CUFF_REV)
+                {
+                    if (trim_point == -1)
+                        trim_point = l_exon_3->g_left();
+                    else if (l_exon_3->g_left() < trim_point)
+                        ok_clip_leader = false;
+                }
+                else 
+                {
+                    if (trim_point == -1)
+                        trim_point = l_exon_3->g_right();
+                    else if (l_exon_3->g_right() > trim_point)
+                        ok_clip_leader = false;
+                }
+            }
+            
+            if (ok_clip_leader)
+            {
+                group_leader = possible_leader;
+                group_exon_3 = l_exon_3;
+                break;
+            }
+            ++l_itr;
+        }
+        
+        if (!group_leader || !group_exon_3)
+            continue;
+        
+        for (size_t j = 0; j < group.size(); ++j)
+        {
+            const AugmentedCuffOp* exon_3 = NULL;
+            int end_diff = 0;
+            if (group_leader->strand() == CUFF_REV)
+            {
+                exon_3 = &(group[j].second->augmented_ops().front());
+                end_diff = group_exon_3->g_left() - exon_3->g_left();
+            }
+            else 
+            {
+                exon_3 = &(group[j].second->augmented_ops().back());                
+                end_diff = exon_3->g_right() - group_exon_3->g_right();
+            }
+            
+            if (end_diff > 0)
+            {
+                // leader
+                //     follower
+                group[j].second->trim_3(end_diff);
+            }
+            else if (end_diff < 0)
+            {
+                //        leader
+                //   follower
+                group[j].second->extend_3(-end_diff);
+            }
+        }
+    }
+    
+    
+	return;
+	
+}
diff --git a/src/filters.h b/src/filters.h
index 7fff718..ed04859 100644
--- a/src/filters.h
+++ b/src/filters.h
@@ -18,7 +18,9 @@
 #include "genes.h"
 
 void filter_junk_isoforms(vector<shared_ptr<Abundance> >& transcripts,
-						  vector<double>& abundances);
+						  vector<double>& abundances,
+                          const vector<shared_ptr<Abundance> >& mapped_transcripts,
+                          double locus_mass);
 
 
 void filter_introns(int bundle_length,
@@ -35,4 +37,6 @@ void filter_junk_genes(vector<Gene>& genes);
 
 void filter_hits(int bundle_length, int bundle_left, vector<Scaffold>& hits);
 
+void clip_by_3_prime_dropoff(vector<Scaffold>& scaff);
+
 #endif
diff --git a/src/genes.cpp b/src/genes.cpp
index e89b8e6..db29bb7 100644
--- a/src/genes.cpp
+++ b/src/genes.cpp
@@ -30,10 +30,13 @@ int get_next_isoform_id()
 }
 
 void Isoform::get_gtf(vector<string>& gff_recs, 
-					  const RefSequenceTable& rt) const
+					  const RefSequenceTable& rt,
+                      set<AugmentedCuffOp>* hit_introns) const
 {
 	const char* ref_name = rt.get_name(_scaffold.ref_id());
 	
+    assert (ref_name != NULL);
+    
 	const char* strand_str = NULL;
 	if (_scaffold.strand() == CUFF_STRAND_UNKNOWN)
 		strand_str = ".";
@@ -49,8 +52,10 @@ void Isoform::get_gtf(vector<string>& gff_recs,
 	
 	char buf[2048];	
 
-	sprintf(buf, 
-			"%s\tCufflinks\ttranscript\t%d\t%d\t%d\t%s\t.\tgene_id \"%s\"; transcript_id \"%s\"; FPKM \"%10.10lf\"; frac \"%lf\"; conf_lo \"%lf\"; conf_hi \"%lf\"; cov \"%lf\";\n",
+	if (hit_introns != NULL)
+    {
+        sprintf(buf, 
+			"%s\tCufflinks\ttranscript\t%d\t%d\t%d\t%s\t.\tgene_id \"%s\"; transcript_id \"%s\"; FPKM \"%10.10lf\"; frac \"%lf\"; conf_lo \"%lf\"; conf_hi \"%lf\"; cov \"%lf\"; full_read_support \"%s\";\n",
 			ref_name,
 			_scaffold.left() + 1,
 			_scaffold.right(), // GTF intervals are inclusive on both ends, but ours are half-open
@@ -62,7 +67,28 @@ void Isoform::get_gtf(vector<string>& gff_recs,
 			_fraction,
 			_confidence.low, 
 			_confidence.high,
-			_coverage);
+			_coverage,
+            (_scaffold.has_struct_support(*hit_introns)) ? "yes":"no");
+    }
+    else
+    {
+        sprintf(buf, 
+                "%s\tCufflinks\ttranscript\t%d\t%d\t%d\t%s\t.\tgene_id \"%s\"; transcript_id \"%s\"; FPKM \"%10.10lf\"; frac \"%lf\"; conf_lo \"%lf\"; conf_hi \"%lf\"; cov \"%lf\";\n",
+                ref_name,
+                _scaffold.left() + 1,
+                _scaffold.right(), // GTF intervals are inclusive on both ends, but ours are half-open
+                score,
+                strand_str,
+                gene_id().c_str(),
+                trans_id().c_str(),
+                _FPKM,
+                _fraction,
+                _confidence.low, 
+                _confidence.high,
+                _coverage);
+    }
+    
+    
 	gff_recs.push_back(buf);
 	
 	int exon_num = 1;
diff --git a/src/genes.h b/src/genes.h
index e9ce6bb..428855e 100644
--- a/src/genes.h
+++ b/src/genes.h
@@ -37,14 +37,17 @@ public:
 			double fraction = 0.0,
 			ConfidenceInterval ci = ConfidenceInterval(),
 			double cov = 0.0,
+            double est_frag_count = 0.0,
 			double fmi = 0.0,
-			AbundanceStatus status = NUMERIC_FAIL) :
+			AbundanceStatus status = NUMERIC_FAIL,
+			string ref_gene_id = "") :
 		_scaffold(s),
 		_FPKM(FPKM),
 		_eff_len(eff_len),
 		_fraction(fraction),
 		_confidence(ci),
 		_coverage(cov),
+        _estimated_count(est_frag_count),
 		_FMI(fmi),
 		_status(status)
 	{
@@ -53,14 +56,16 @@ public:
 		char trans_id_str[256];
 		if (_scaffold.annotated_trans_id() != "")
 			strncpy(trans_id_str, _scaffold.annotated_trans_id().c_str(), 255);
+		else if (gid == -1)
+			sprintf(trans_id_str, "%s.%s.%d", user_label.c_str(), ref_gene_id.c_str(), tid);
 		else
 			sprintf(trans_id_str, "%s.%d.%d", user_label.c_str(), gid, tid);
 		
 		_trans_id = trans_id_str;
 		
 		char gene_id_str[256];
-		if (_scaffold.annotated_gene_id() != "")
-			strncpy(gene_id_str, _scaffold.annotated_gene_id().c_str(), 255);
+		if(gid == -1)
+			strncpy(gene_id_str, ref_gene_id.c_str(), 255);
 		else
 			sprintf(gene_id_str, "%s.%d", user_label.c_str(), gid);
 		_gene_id = gene_id_str;
@@ -92,11 +97,17 @@ public:
 	int ID() const { return _id; }
 
 	void get_gtf(vector<string>& gtf_recs, 
-				 const RefSequenceTable& rt) const;
+				 const RefSequenceTable& rt,
+                 set<AugmentedCuffOp>* hit_introns=NULL) const;
 	
+	void gene_id(string& gid) { _gene_id = gid; }
 	const string& gene_id() const { return _gene_id; }
 	const string& trans_id() const {return _trans_id; }
 	
+	bool is_ref_trans() const { return _scaffold.is_ref(); }
+	
+    double estimated_count() const { return _estimated_count; }
+    void estimated_count(double est) { _estimated_count = est; }
 private:
 	
 	Scaffold _scaffold;
@@ -105,6 +116,7 @@ private:
 	double _fraction;
 	ConfidenceInterval _confidence;
 	double _coverage;
+    double _estimated_count;
 	double _FMI;
 	int _id;
 	string _gene_id;
@@ -123,9 +135,7 @@ public:
 		_FPKM(FPKM),
 		_confidence(ci),
 		_status(status)
-	{
-		_id = get_next_gene_id();
-		
+	{		
 		vector<Scaffold> scaffolds;
 		for (size_t i = 0; i < isoforms.size(); ++i)
 			scaffolds.push_back(isoforms[i].scaffold());
@@ -148,12 +158,46 @@ public:
 	AbundanceStatus status() const { return _status; }
 	void   status(AbundanceStatus status) { _status = status; }
 	
-	int ID() const { return _id; }
 	int left() const { return _left; }
 	int right() const { return _right; }
 	
 	const string& gene_id() const { return _gene_id; }
 	
+	bool has_ref_trans() const
+	{
+		foreach (const Isoform& iso, _isoforms)
+		{
+			if (iso.is_ref_trans())
+				return true;
+		}
+		return false;
+	}
+    
+    double estimated_count() const 
+    {
+        double est = 0.0;
+        foreach (const Isoform& iso, _isoforms)
+		{
+			est += iso.estimated_count(); 
+		}
+		return est;
+    }
+    
+    double effective_length() const 
+    {
+        double eff = 0.0;
+        double total_fpkm = 0;
+        foreach (const Isoform& iso, _isoforms)
+		{
+			eff += iso.FPKM() * iso.effective_length();
+            total_fpkm += iso.FPKM();
+		}
+        if (total_fpkm)
+            return eff / total_fpkm;
+		else
+            return 0;
+    }
+	
 private:
 	
 	vector<Isoform> _isoforms;
diff --git a/src/gff.cpp b/src/gff.cpp
index a529272..22ffa68 100644
--- a/src/gff.cpp
+++ b/src/gff.cpp
@@ -5,13 +5,24 @@ GffNames* GffObj::names=NULL;
 //global set of feature names, attribute names etc.
 // -- common for all GffObjs in current application!
 
-const uint GFF_MAX_LOCUS = 4000000; //longest known gene in human is ~2.2M, UCSC claims a gene for mouse of ~ 3.1 M
-const uint GFF_MAX_EXON  =   20000; //longest known exon in human is ~11K
-const uint GFF_MAX_INTRON= 1600000;
-
+const uint GFF_MAX_LOCUS = 7000000; //longest known gene in human is ~2.2M, UCSC claims a gene for mouse of ~ 3.1 M
+const uint GFF_MAX_EXON  =   30000; //longest known exon in human is ~11K
+const uint GFF_MAX_INTRON= 6000000;
+bool gff_show_warnings = false; //global setting, set by GffReader->showWarnings()
 const int gff_fid_mRNA=0;
-const int gff_fid_exon=1;
-const int gff_fid_CDS=2; //never really used in GffObj ftype_id or subftype_id
+const int gff_fid_transcript=1;
+const int gff_fid_exon=2;
+const int gff_fid_CDS=3; //never really used in GffObj ftype_id or subftype_id
+const uint gfo_flag_HAS_ERRORS       = 0x00000001;
+const uint gfo_flag_CHILDREN_PROMOTED= 0x00000002;
+const uint gfo_flag_IS_GENE          = 0x00000004;
+const uint gfo_flag_IS_TRANSCRIPT    = 0x00000008;
+const uint gfo_flag_FROM_GFF3        = 0x00000010;
+const uint gfo_flag_BY_EXON          = 0x00000020; //created by subfeature (exon) directly
+const uint gfo_flag_DISCARDED        = 0x00000100;
+const uint gfo_flag_LST_KEEP         = 0x00000200;
+const uint gfo_flag_LEVEL_MSK        = 0x00FF0000;
+const byte gfo_flagShift_LEVEL           = 16;
 
 void gffnames_ref(GffNames* &n) {
   if (n==NULL) n=new GffNames();
@@ -31,31 +42,95 @@ int gfo_cmpByLoc(const pointer p1, const pointer p2) {
  if (g1.gseq_id==g2.gseq_id) {
              if (g1.start!=g2.start)
                     return (int)(g1.start-g2.start);
-               else if (g1.end!=g2.end) 
-                           return (int)(g1.end-g2.end);
-                      else return strcmp(g1.getID(), g2.getID());
+               else if (g1.getLevel()!=g2.getLevel())
+                        return (int)(g1.getLevel()-g2.getLevel());
+                    else
+                        if (g1.end!=g2.end)
+                              return (int)(g1.end-g2.end);
+                        else return strcmp(g1.getID(), g2.getID());
              }
              else return (int)(g1.gseq_id-g2.gseq_id);
 }
 
+char* GffLine::extractAttr(const char* pre, bool caseStrict, bool enforce_GTF2) {
+ //parse a key attribute and remove it from the info string
+ //(only works for attributes that have values following them after ' ' or '=')
+ static const char GTF2_ERR[]="Error parsing attribute %s ('\"' required) at GTF line:\n%s\n";
+ int lpre=strlen(pre);
+ char cend=pre[lpre-1];
+ char* pos = (caseStrict) ? strstr(info, pre) : strifind(info, pre);
+ if (pos==NULL) return NULL;
+ char* findstart=info;
+ //require word boundary on the left:
+ while (pos!=NULL && pos!=info && *(pos-1)!=';' && *(pos-1)!=' ') {
+    findstart=pos+lpre;
+    pos = (caseStrict) ? strstr(findstart, pre) : strifind(findstart, pre);
+    }
+ if (pos==NULL) return NULL;
+ if (cend!=' ' && cend!='=') {
+    //require word boundary on the right:
+    while (pos!=NULL && *(pos+lpre)!=' ' && *(pos+lpre)!='=') {
+       findstart=pos+lpre;
+       pos = (caseStrict) ? strstr(findstart, pre) : strifind(findstart, pre);
+       }
+    }
+ if (pos==NULL) return NULL;
+ char* vp=pos+lpre;
+ while (*vp==' ') vp++;
+ if (*vp==';' || *vp==0)
+      GError("Error parsing value of GFF attribute \"%s\", line:\n%s\n", pre, dupline);
+ bool dq_enclosed=false; //value string enclosed by double quotes
+ if (*vp=='"') {
+     dq_enclosed=true;
+     vp++;
+     }
+ if (enforce_GTF2 && !dq_enclosed)
+      GError(GTF2_ERR,pre, dupline);
+ char* vend=vp;
+ if (dq_enclosed) {
+    while (*vend!='"' && *vend!=';' && *vend!=0) vend++;
+    }
+ else {
+    while (*vend!=';' && *vend!=0) vend++;
+    }
+ if (enforce_GTF2 && *vend!='"')
+     GError(GTF2_ERR, pre, dupline);
+ char *r=Gstrdup(vp, vend-1);
+ //-- now remove this attribute from the info string
+ while (*vend!=0 && (*vend=='"' || *vend==';' || *vend==' ')) vend++;
+ if (*vend==0) vend--;
+ for (char *src=vend, *dest=pos;;src++,dest++) {
+   *dest=*src;
+   if (*src==0) break;
+   }
+ return r;
+}
+
 static char fnamelc[128];
 
 GffLine::GffLine(GffReader* reader, const char* l) {
- //line=Gstrdup(l);
  llen=strlen(l);
  GMALLOC(line,llen+1);
- memcpy((void*)line, (void*)l, llen+1);
+ memcpy(line, l, llen+1);
+ GMALLOC(dupline, llen+1);
+ memcpy(dupline, l, llen+1);
  skip=true;
  gseqname=NULL;
  track=NULL;
  ftype=NULL;
  info=NULL;
- Parent=NULL;
+ _parents=NULL;
+ _parents_len=0;
+ num_parents=0;
+ parents=NULL;
+ is_gff3=false;
  is_cds=false;
- is_mrna=false;
+ is_transcript=false;
  is_exon=false;
+ is_gene=false;
  exontype=0;
- gname=NULL;
+ gene_id=NULL;
+ gene_name=NULL;
  qstart=0;
  qend=0;
  qlen=0;
@@ -103,117 +178,136 @@ GffLine::GffLine(GffReader* reader, const char* l) {
      GError("Error parsing strand (%c) from GFF line:\n%s\n",strand,l);
  phase=*t[7]; // must be '.', '0', '1' or '2'
  ID=NULL;
- Parent=NULL;
  // exon/CDS/mrna filter
  strncpy(fnamelc, ftype, 127);
  fnamelc[127]=0;
  strlower(fnamelc); //convert to lower case
- /*
- if (strstr(fnamelc, "locus")!=NULL || strstr(fnamelc, "gene")!=NULL) {
-   return; //discard higher-level hierarchical elements
-   }
- */
+ bool is_t_data=false;
  if (strstr(fnamelc, "utr")!=NULL) {
    exontype=exgffUTR;
    is_exon=true;
+   is_t_data=true;
    }
   else if (strstr(fnamelc, "exon")!=NULL) {
    exontype=exgffExon;
    is_exon=true;
+   is_t_data=true;
    }
   else if (strstr(fnamelc, "stop") && 
-      ((strstr(fnamelc, "codon")!=NULL) || strstr(fnamelc, "cds")!=NULL)){
+      (strstr(fnamelc, "codon") || strstr(fnamelc, "cds"))){
    exontype=exgffStop;
    is_cds=true; //though some place it outside the last CDS segment
+   is_t_data=true;
    }
   else if (strstr(fnamelc, "start") && 
       ((strstr(fnamelc, "codon")!=NULL) || strstr(fnamelc, "cds")!=NULL)){
    exontype=exgffStart;
    is_cds=true;
+   is_t_data=true;
    }
  else if (strcmp(fnamelc, "cds")==0) {
    exontype=exgffCDS;
    is_cds=true;
+   is_t_data=true;
    }
- else { //is_mrna is set only if the *current* line is a mRNA or transcript
-   is_mrna=(strcmp(fnamelc,"mrna")==0 ||
-          strcmp(fnamelc,"transcript")==0);
+ else if (endsWith(fnamelc, "gene") || startsWith(fnamelc, "gene")) {
+   is_gene=true;
+   is_t_data=true; //because its name will be attached to parented transcripts
    }
-
- if (reader->mrnaOnly) {
-   if (!is_mrna && !is_cds && !is_exon) {
-                  if (reader->gff_warns) 
-                        GMessage("skipping non-mRNA line: %s\n", l);
-                  return; //skip this line, unwanted feature name
-                  }
+ else if (endsWith(fnamelc,"rna") || endsWith(fnamelc,"transcript")) {
+   is_transcript=true;
+   is_t_data=true;
    }
- char* hasGffID=strifind(info,"ID=");
- char* hasGffParent=strifind(info,"Parent=");
- bool isGffLine=(hasGffID!=NULL || hasGffParent!=NULL);
- //p=strstr(info,"ID=");
- if (isGffLine) {
-   //parse as GFF3
-    if (hasGffID!=NULL) { //has ID attr
-       ID=hasGffID+3;
-       p=ID;
-       while (*p!=';' && *p!=0) p++;
-       ID=Gstrdup(ID, p-1);
-       //look for a name attr too:
-       p=strstr(info,"Name=");
-       if (p!=NULL) {
-         gname=p+5;
-         p=gname;
-         while (*p!=';' && *p!=0) p++;
-         gname=Gstrdup(gname, p-1);
-         }
-       }
 
-   //discard the parent for mRNA features (e.g. genes, loci etc.)
-   if (reader->mrnaOnly && is_mrna) {
-        if (reader->gff_warns) 
-              GMessage("ignore parent parsing for mRNA line: %s\n", l);
-        p=NULL;
+if (reader->transcriptsOnly && !is_t_data) {
+        char* id=extractAttr("ID=");
+        if (id==NULL) id=extractAttr("transcript_id");
+        //GMessage("Discarding non-transcript line:\n%s\n",l);
+        if (id!=NULL) {
+          reader->discarded_ids.Add(id, new int(1));
+          GFREE(id);
+          }
+        return; //skip this line, unwanted feature name
         }
-      else if (hasGffParent!=NULL) { 
-        //has Parent attr
-         Parent=hasGffParent+7;
+ ID=extractAttr("ID=");
+ char* Parent=extractAttr("Parent=");
+ is_gff3=(ID!=NULL || Parent!=NULL);
+ if (is_gff3) {
+   //parse as GFF3
+    if (ID!=NULL) {
+       //has ID attr so it's likely to be a parent feature
+       //look for explicit gene name
+       gene_name=extractAttr("gene_name=",false);
+       if (gene_name==NULL) {
+           gene_name=extractAttr("geneName=",false);
+           if (gene_name==NULL) {
+               gene_name=extractAttr("gene_sym=",false);
+               if (gene_name==NULL) {
+                   gene_name=extractAttr("gene=",false);
+                   }
+               }
+           }
+       gene_id=extractAttr("geneID=",false);
+       if (gene_id==NULL) {
+          gene_id=extractAttr("gene_id=",false);
+          }
+       if (is_gene) {
+         //special case: keep the Name and ID attributes of the gene feature
+         if (gene_name==NULL)
+              gene_name=extractAttr("Name=");
+         if (gene_id==NULL) //the ID is also gene_id in this case
+              gene_id=Gstrdup(ID);
+         //skip=false;
+         //return;
+         GFREE(Parent); //TMI, we really don't care about gene Parents?
+         } //gene feature
+       }// has GFF3 ID
+   if (Parent!=NULL) {
+        //keep Parent attr
+         //parse multiple parents
+         num_parents=1;
          p=Parent;
-         while (*p!=';' && *p!=0) p++;
-         Parent=Gstrdup(Parent, p-1);
+         int last_delim_pos=-1;
+         while (*p!=';' && *p!=0) {
+             if (*p==',' && *(p+1)!=0 && *(p+1)!=';') {
+                 num_parents++;
+                 last_delim_pos=(p-Parent);
+                 }
+             p++;
+             }
+         _parents_len=p-Parent+1;
+         _parents=Parent;
+         GMALLOC(parents, num_parents*sizeof(char*));
+         parents[0]=_parents;
+         int i=1;
+         if (last_delim_pos>0) {
+           for (p=_parents+1;p<=_parents+last_delim_pos;p++) {
+              if (*p==',') {
+                 char* ep=p-1;
+                 while (*ep==' ' && ep>_parents) ep--;
+                 *(ep+1)=0; //end the string there
+                 parents[i]=p+1;
+                 i++;
+                 }
+              }
+           }
+         } //has Parent field
+   } //GFF3
+  else { // GTF-like expected
+   Parent=extractAttr("transcript_id");
+   if (Parent!=NULL) { //GTF2 format detected
+     if (is_transcript) {
+         // atypical GTF with a parent transcript line declared
+         ID=Parent;
+         Parent=NULL;
          }
-   }
-  else {
-   //not GFF3, try GTF-like (incl. jigsaw)
-   p=strstr(info,"transcript_id");
-   if (p!=NULL) { //GTF detected
-     p+=13;//length of 'transcript_id'
-     //requires dbl quotes!
-     while (*p!='"' && *p!=0) p++;
-     if (*p==0) GError("Error parsing transcript_id (double quotes expected) at GTF line:\n%s\n",l);
-     p++;
-     Parent=p;
-     while (*p!='"' && *p!=0) p++;
-     if (*p==0) GError("Error parsing transcript_id (ending double quotes expected) at GTF line:\n%s\n",l);
-     if (is_mrna) {
-        // RGASP GTF exception: a parent "transcript" line 
-        ID=Gstrdup(Parent, p-1); //special GTF with parent line
-        Parent=NULL;
-        }
-       else {
-        Parent=Gstrdup(Parent, p-1); //typical GTF, no explicit parent line
-        }
-     //check for gene_id
-     p=strstr(info,"gene_id");
-     if (p!=NULL) {
-       p+=7;//skip 'gene_id'
-       while (*p!='"' && *p!=0) p++;
-       if (*p==0) GError("Error parsing gene_id (double quotes expected) at GTF line:\n%s\n",l);
-       p++;
-       gname=p;
-       while (*p!='"' && *p!=0) p++;
-       if (*p==0) GError("Error parsing gene_id (ending double quotes expected) at GTF line:\n%s\n",l);
-       gname=Gstrdup(gname, p-1);
-       }
+     gene_id=extractAttr("gene_id"); // for GTF this is the only attribute accepted as geneID
+     gene_name=extractAttr("gene_name");
+     if (gene_name==NULL) {
+           gene_name=extractAttr("gene_sym");
+           if (gene_name==NULL)
+               gene_name=extractAttr("gene");
+           }
      //prepare for parseAttr by adding '=' character instead of spaces for all attributes
      //after the attribute name
      p=info;
@@ -221,19 +315,19 @@ GffLine::GffLine(GffReader* reader, const char* l) {
      bool nsp=false; //non-space found after last delim
      while (*p!=0) {
        if (*p==' ') {
-         if (nsp && noed) {
+          if (nsp && noed) {
              *p='=';
              noed=false;
              p++;
              continue;
              }
-         }
-          else nsp=true;
+           }
+         else nsp=true; //non-space
        if (*p==';') { noed=true; nsp=false; }
        p++;
        }
-     } //basic GTF detected (no parent line)
-    else {// check for jigsaw or cufflinks format
+     } //GTF2 detected (no parent line)
+    else {// Parent is NULL, check for jigsaw format or other pre-GTF2 format
      //char* fexon=strstr(fnamelc, "exon");
      //if (fexon!=NULL) {
      if (exontype==exgffExon) {
@@ -241,94 +335,112 @@ GffLine::GffLine(GffReader* reader, const char* l) {
           is_cds=true;
           strcpy(track,"jigsaw");
           p=strchr(info,';');
-          if (p==NULL) Parent=Gstrdup(info);
-           else { Parent=Gstrdup(info,p-1); info=p+1;  }
+          if (p==NULL) { Parent=Gstrdup(info); info=NULL; }
+           else { Parent=Gstrdup(info,p-1);
+                  info=p+1;
+                }
           }
-        else if ((i=strcspn(info,"; \t\n\r"))<=(int)(strlen(info)+1)) {//one word ID
+        } //exon feature?
+        if (Parent==NULL && exontype>=exgffCDS &&
+               (i=strcspn(info,"; \t\n\r"))<=(int)(strlen(info)+1)) {
+          //one word ID ? really desperate attempt to parse it here
           Parent=Gstrdup(info,info+i-1);
+          info=NULL; //discard anything else on the line
           }
-        }
-      else GError("Error parsing Parent/ID at input line:\n%s\n",l);
      }
-   } //not GFF3
- //parse other potentially useful features
- p=strstr(info,"Target=");
- if (p!=NULL) { //has Target attr
-   p+=7;
-   while (*p!=';' && *p!=0 && *p!=' ') p++;
-   if (*p!=' ') {
-      GError("Error parsing target coordinates from GFF line:\n%s\n",l);
+   if (Parent!=NULL) { //GTF transcript_id for exon/CDS feature
+      _parents=Parent;
+      GMALLOC(parents,sizeof(char*));
+      num_parents=1;
+      parents[0]=_parents;
       }
-   if (!parseUInt(p,qstart))
-     GError("Error parsing target start coordinate from GFF line:\n%s\n",l);
-   if (*p!=' ') {
-      GError("Error parsing next target coordinate from GFF line:\n%s\n",l);
+   } //GTF-like
+
+ //parse other potentially useful features
+ if (is_gff3) {
+   if ((p=strstr(info,"Target="))!=NULL) { //has Target attr
+      p+=7;
+      while (*p!=';' && *p!=0 && *p!=' ') p++;
+      if (*p!=' ') {
+         GError("Error parsing target coordinates from GFF line:\n%s\n",l);
+         }
+      if (!parseUInt(p,qstart))
+         GError("Error parsing target start coordinate from GFF line:\n%s\n",l);
+      if (*p!=' ') {
+         GError("Error parsing next target coordinate from GFF line:\n%s\n",l);
+         }
+      p++;
+      if (!parseUInt(p,qend))
+         GError("Error parsing target end coordinate from GFF line:\n%s\n",l);
       }
-   p++;
-   if (!parseUInt(p,qend))
-     GError("Error parsing target end coordinate from GFF line:\n%s\n",l);
-   }
- else {
-   p=strifind(info,"Qreg=");
-   if (p!=NULL) { //has Qreg attr
-     p+=5;
-     if (!parseUInt(p,qstart))
-       GError("Error parsing target start coordinate from GFF line:\n%s\n",l);
-     if (*p!='-') {
-        GError("Error parsing next target coordinate from GFF line:\n%s\n",l);
-        }
-     p++;
-     if (!parseUInt(p,qend))
-       GError("Error parsing target end coordinate from GFF line:\n%s\n",l);
-     if (*p=='|') {
+   if ((p=strifind(info,"Qreg="))!=NULL) { //has Qreg attr
+       p+=5;
+       if (!parseUInt(p,qstart))
+         GError("Error parsing target start coordinate from GFF line:\n%s\n",l);
+       if (*p!='-') {
+          GError("Error parsing next target coordinate from GFF line:\n%s\n",l);
+          }
        p++;
-       if (!parseUInt(p,qlen))
-         GError("Error parsing target length from GFF Qreg|: \n%s\n",l);
-       }
-     }//has Qreg attr
-   }
- if (qlen==0 && (p=strifind(info,"Qlen="))!=NULL) {
-   p+=5;
-   if (!parseUInt(p,qlen))
-       GError("Error parsing target length from GFF Qlen:\n%s\n",l);
-   }
+       if (!parseUInt(p,qend))
+         GError("Error parsing target end coordinate from GFF line:\n%s\n",l);
+       if (*p=='|' || *p==':') {
+         p++;
+         if (!parseUInt(p,qlen))
+           GError("Error parsing target length from GFF Qreg|: \n%s\n",l);
+         }
+       }//has Qreg attr
+   if (qlen==0 && (p=strifind(info,"Qlen="))!=NULL) {
+     p+=5;
+     if (!parseUInt(p,qlen))
+         GError("Error parsing target length from GFF Qlen:\n%s\n",l);
+     }
+   }//parsing some useful attributes in GFF3 records
+ if (ID==NULL && parents==NULL) {
+      if (reader->gff_warns)
+          GMessage("Warning: could not parse ID or Parent from GFF line:\n%s\n",dupline);
+      return; //skip
+      }
  skip=false;
 }
 
 int GffObj::addExon(GffReader* reader, GffLine* gl, bool keepAttr, bool noExonAttr) {
   //this will make sure we have the right subftype_id!
   int subf_id=-1;
-  if (ftype_id==gff_fid_mRNA) { //for mRNAs only parse known subfeatures!
-     if (subftype_id<0) subftype_id=gff_fid_exon; 
+  //if (ftype_id==gff_fid_mRNA) { //for mRNAs only parse known subfeatures!
+  if (isTranscript()) {
+     if (exon_ftype_id<0) {//exon_ftype_id=gff_fid_exon;
+          if (gl->exontype>0) exon_ftype_id=gff_fid_exon;
+                         else exon_ftype_id=names->feats.addName(gl->ftype);
+          }
      //any recognized mRNA segment gets the generic "exon" type (also applies to CDS)
-     if (gl->exontype==0 && !gl->is_mrna) {
+     if (gl->exontype==0 && !gl->is_transcript) {
           //extraneous mRNA feature, discard
-          if (reader->gff_warns) 
-            GMessage("Warning: discarding unrecognized mRNA subfeature %s of %s\n", 
+          if (reader->gff_warns)
+            GMessage("Warning: discarding unrecognized transcript subfeature %s of %s\n", 
                 gl->ftype, gffID);
           return -1;
           }
      }
   else { //non-mRNA parent feature, check this subf type
     subf_id=names->feats.addName(gl->ftype);
-    if (subftype_id<0 || exons.Count()==0) //never assigned a subfeature type before (e.g. first exon being added)
-       subftype_id=subf_id;
+    if (exon_ftype_id<0 || exons.Count()==0) //never assigned a subfeature type before (e.g. first exon being added)
+       exon_ftype_id=subf_id;
      else {
-       if (subftype_id!=subf_id) {
+       if (exon_ftype_id!=subf_id) {
          //if (subftype_id==ftype_id && exons.Count()==1 && exons[0]->start==start && exons[0]->end==end) {
-         if (subftype_id==ftype_id && exons.Count()==1 && exons[0]->start==start && exons[0]->end==end) {
+         if (exon_ftype_id==ftype_id && exons.Count()==1 && exons[0]->start==start && exons[0]->end==end) {
             //the existing exon was just a dummy one created by default, discard it
             exons.Clear();
             covlen=0;
-            subftype_id=subf_id; //allow the new subfeature to completely takeover
+            exon_ftype_id=subf_id; //allow the new subfeature to completely takeover
             }
          else { //multiple subfeatures, prefer those with 
              if (reader->gff_warns)
                GMessage("GFF Warning: multiple subfeatures (%s and %s) found for %s, discarding ", 
-                  names->feats.getName(subf_id), names->feats.getName(subftype_id),gffID);
+                  names->feats.getName(subf_id), names->feats.getName(exon_ftype_id),gffID);
             if (gl->exontype!=0) { //new feature is an exon, discard previously parsed subfeatures
-               if (reader->gff_warns) GMessage("%s.\n", names->feats.getName(subftype_id));
-               subftype_id=subf_id;
+               if (reader->gff_warns) GMessage("%s.\n", names->feats.getName(exon_ftype_id));
+               exon_ftype_id=subf_id;
                exons.Clear();
                covlen=0;
                }
@@ -340,8 +452,6 @@ int GffObj::addExon(GffReader* reader, GffLine* gl, bool keepAttr, bool noExonAt
          } //incoming subfeature is of different type
        } //new subfeature type
     } //non-mRNA parent
-  //if (gl->exontype==exgffUTR || gl->exontype==exgffStop) 
-  //    udata=1; //merge 0-distance segments
   int eidx=addExon(gl->fstart, gl->fend, gl->score, gl->phase,
          gl->qstart,gl->qend, gl->is_cds, gl->exontype);
   if (eidx<0) return eidx; //this should never happen
@@ -361,12 +471,12 @@ int GffObj::addExon(GffReader* reader, GffLine* gl, bool keepAttr, bool noExonAt
 int GffObj::addExon(uint segstart, uint segend, double sc, char fr, int qs, int qe, bool iscds, char exontype) {
   if (exons.Count()==0) {
       if (iscds) isCDS=true; //for now, assume CDS only if first "exon" given is a CDS
-      if (subftype_id<0) {
-         subftype_id = (ftype_id==gff_fid_mRNA) ? gff_fid_exon : ftype_id;
+      if (exon_ftype_id<0) {
+         exon_ftype_id = isTranscript() ? gff_fid_exon : ftype_id;
          }
       }
   //special treatment of start/stop codon features, they might be broken/split between exons 
-  //but some providers give the wrong end coordinate as start+2 (e.g. in UCSC GTF)
+  //and in that case some providers will still give the wrong end coordinate as start+2 (e.g. UCSC)
   //so we should not trust the end coordinate for such features
   if (exontype==exgffStart || exontype==exgffStop) {
      if (strand=='-') segstart=segend;
@@ -399,16 +509,20 @@ int GffObj::addExon(uint segstart, uint segend, double sc, char fr, int qs, int
       int ovlen=0;
       int oi=exonOverlapIdx(segstart, segend, &ovlen);
       if (oi>=0) { //overlap existing segment
+         if (ovlen==0) {
+              //adjacent segments will be merged
+              if ((exons[oi]->exontype==exgffUTR && exontype==exgffCDS) ||
+                  (exons[oi]->exontype==exgffCDS && exontype==exgffUTR)) {
+                    expandExon(oi, segstart, segend, exgffCDSUTR, sc, fr, qs, qe);
+                    return oi;
+                    }
+             }
          //only allow this for CDS within exon, stop_codon within exon, stop_codon within UTR,
          //                   start_codon within CDS or stop_codon within CDS
-         /*
-         GMessage("Exon overlap: existing %d:%d-%d vs incoming %d:%d-%d\n",
-                    exons[oi]->exontype, exons[oi]->start, exons[oi]->end, exontype, segstart, segend);
-        */
         if (exons[oi]->exontype>exontype && 
              exons[oi]->start<=segstart && exons[oi]->end>=segend &&
              !(exons[oi]->exontype==exgffUTR && exontype==exgffCDS)) {
-              //larger segment given first, now the smaller included one
+              //larger segment given first, now the smaller included one is redundant
               return oi; //only used to store attributes from current GffLine
               }
         if (exontype>exons[oi]->exontype && 
@@ -416,22 +530,23 @@ int GffObj::addExon(uint segstart, uint segend, double sc, char fr, int qs, int
              !(exontype==exgffUTR && exons[oi]->exontype==exgffCDS)) {
                //smaller segment given first, so we have to enlarge it
               expandExon(oi, segstart, segend, exontype, sc, fr, qs, qe); 
-                //this must also check for overlapping next exon (oi+1) 
+                //this should also check for overlapping next exon (oi+1) ?
               return oi;
               }
         //there is also the special case of "ribosomal slippage exception" (programmed frameshift)
         //where two CDS segments may actually overlap for 1 or 2 bases, but there should be only one encompassing exon
         //if (ovlen>2 || exons[oi]->exontype!=exgffCDS || exontype!=exgffCDS) {
         // --> had to relax this because of some weird UCSC annotations with exons partially overlapping the CDS segments
-        if (ovlen>2) {
-           //important structural warning:
-           GMessage("Warning: discarding overlapping feature segment (%d-%d) (vs %d-%d (%s)) for GFF ID %s on %s\n", 
+        if (ovlen>2 && exons[oi]->exontype!=exgffUTR && exontype!=exgffUTR) {
+           //important structural warning, will always print:
+           if (gff_show_warnings) 
+               GMessage("GFF Warning: discarding overlapping feature segment (%d-%d) (vs %d-%d (%s)) for GFF ID %s on %s\n", 
                segstart, segend, exons[oi]->start, exons[oi]->end, getSubfName(), gffID, getGSeqName());
-           hasErrors=true;
-           return false; //segment NOT added
+           hasErrors(true);
+           return -1; //segment NOT added
            }
           // else add the segment if the overlap is small and between two CDS segments
-          //we might want to add an attribute here with the slippage coordinate and size?
+          //TODO: we might want to add an attribute here with the slippage coordinate and size?
         }//overlap of existing segment
        } //check for overlap
    // --- no overlap, or accepted micro-overlap (ribosomal slippage)
@@ -439,11 +554,12 @@ int GffObj::addExon(uint segstart, uint segend, double sc, char fr, int qs, int
    GffExon* enew=new GffExon(segstart, segend, sc, fr, qs, qe, exontype);
    int eidx=exons.Add(enew);
    if (eidx<0) {
-    //this would actually be acceptable if "exons" are in fact just isoforms with an internal exon skipping event
-    //      (and the Parent is a "gene")
-     GMessage("GFF Warning: failed adding segment %d-%d for GFF ID %s!\n",
+    //this would actually be acceptable if the object is a "Gene" and "exons" are in fact isoforms
+     if (gff_show_warnings) 
+       GMessage("GFF Warning: failed adding segment %d-%d for %s (discarded)!\n",
             segstart, segend, gffID);
      delete enew;
+     hasErrors(true);
      return -1;            
      }
    covlen+=(int)(exons[eidx]->end-exons[eidx]->start)+1;
@@ -466,23 +582,22 @@ void GffObj::expandExon(int oi, uint segstart, uint segend, char exontype, doubl
   covlen-=exons[oi]->len();
   if (segstart<exons[oi]->start)
     exons[oi]->start=segstart;
-  if (qs<exons[oi]->qstart) exons[oi]->qstart=qs;
+  if (qs && qs<exons[oi]->qstart) exons[oi]->qstart=qs;
   if (segend>exons[oi]->end)
     exons[oi]->end=segend;
-  if (qe>exons[oi]->qend) exons[oi]->qend=qe;
+  if (qe && qe>exons[oi]->qend) exons[oi]->qend=qe;
   //warning: score cannot be properly adjusted! e.g. if it's a p-value it's just going to get worse
   if (sc!=0) exons[oi]->score=sc;
   covlen+=exons[oi]->len();
   //if (exons[oi]->exontype< exontype) -- always true
   exons[oi]->exontype = exontype;
-  if (exontype==exgffCDS) exons[oi]->phase=fr; 
+  if (exontype==exgffCDS) exons[oi]->phase=fr;
   //we must check if any more exons are also overlapping this 
   int ni=oi+1; //next exon index after oi
   while (ni<exons.Count() && segend>=exons[ni]->start) { // next segment overlaps new enlarged segment
      //only allow this if next segment is fully included, and a subordinate
      if (exons[ni]->exontype<exontype && exons[ni]->end<=segend) {
-     // should we relax this due to stupid UCSC hg18 files having a start_codon sticking out?
-/*
+/* I guess we have to relax this due to stupid UCSC hg18 files having a start_codon sticking out
 chr1	hg18_knownGene	start_codon	69806911	69806913	0.000000	+	.
 chr1	hg18_knownGene	CDS	69806911	69806912	0.000000	+	0
 chr1	hg18_knownGene	exon	69805456	69806912	0.000000	+	.     
@@ -492,8 +607,9 @@ chr1	hg18_knownGene	exon	69805456	69806912	0.000000	+	.
          exons.Delete(ni);
          }
       else {
-         GMessage("GFF Warning: overlapping feature segment (%d-%d) for GFF ID %s\n", segstart, segend, gffID);
-         hasErrors=true;
+         if (gff_show_warnings) GMessage("GFF Warning: overlapping existing exon(%d-%d) while expanding to %d-%d for GFF ID %s\n",
+                exons[ni]->start, exons[ni]->end, segstart, segend, gffID);
+         //hasErrors(true);
          break;
          }
      }
@@ -528,8 +644,25 @@ void GffObj::removeExon(int idx) {
   if (isCDS) { CDstart=start; CDend=end; }
 }
 
+void GffObj::removeExon(GffExon* p) {
+  for (int idx=0;idx<exons.Count();idx++) {
+     if (exons[idx]==p) {
+        int segstart=exons[idx]->start;
+        int segend=exons[idx]->end;
+        exons.Delete(idx);
+        covlen -= (int)(segend-segstart)+1;
+        start=exons.First()->start;
+        end=exons.Last()->end;
+        if (isCDS) { CDstart=start; CDend=end; }
+        return;
+        }
+     }
+}
+
+
+
 GffObj::GffObj(GffReader *gfrd, GffLine* gffline, bool keepAttr, bool noExonAttr):
-     GSeg(0,0), exons(true,true,false) {
+     GSeg(0,0), exons(true,true,false), children(1,false) {
   xstart=0;
   xend=0;
   xstatus=0;
@@ -537,84 +670,98 @@ GffObj::GffObj(GffReader *gfrd, GffLine* gffline, bool keepAttr, bool noExonAttr
   isCDS=false;
   uptr=NULL;
   ulink=NULL;
+  parent=NULL;
   udata=0;
+  flags=0;
   CDstart=0;
   CDend=0;
   CDphase=0;
-  gname=NULL;
+  geneID=NULL;
+  gene_name=NULL;
   attrs=NULL;
   gffID=NULL;
   track_id=-1;
   gseq_id=-1;
   ftype_id=-1;
-  subftype_id=-1;
-  hasErrors=false;
+  exon_ftype_id=-1;
+  strand='.';
   if (gfrd==NULL)
     GError("Cannot use this GffObj constructor with a NULL GffReader!\n");
   gffnames_ref(names);
   if (gfrd->names==NULL) gfrd->names=names;
-  qlen=0;qstart=0;qend=0;
+  //qlen=0;qstart=0;qend=0;
   gscore=0;
   uscore=0;
   covlen=0;
   qcov=0;
-  if (gffline->Parent!=NULL) {
-    //GTF style -- subfeature given directly
-    //(also possible orphan GFF line)
-    if (gffline->exontype!=0 || gffline->is_mrna) {
-       ftype_id=gff_fid_mRNA; //a new mRNA record starting here
-       subftype_id=gff_fid_exon; //subfeatures MUST be exons-kind
+  start=gffline->fstart;
+  end=gffline->fend;
+  gseq_id=names->gseqs.addName(gffline->gseqname);
+  track_id=names->tracks.addName(gffline->track);
+  strand=gffline->strand;
+  qlen=gffline->qlen;
+  qstart=gffline->qstart;
+  qend=gffline->qend;
+  //setup flags from gffline
+  isCDS=gffline->is_cds; //for now
+  isGene(gffline->is_gene);
+  isTranscript(gffline->is_transcript || gffline->exontype!=0);
+  fromGff3(gffline->is_gff3);
+
+  if (gffline->parents!=NULL) {
+    //GTF style -- create a GffObj directly by subfeature
+    //(also possible orphan GFF3 exon line, or an exon given before its parent (chado))
+    if (gffline->exontype!=0) { //recognized exon-like feature
+       ftype_id=gff_fid_transcript; //so this is some sort of transcript
+       exon_ftype_id=gff_fid_exon; //subfeatures MUST be exons
        }
-     else {
-       //group of other subfeatures of type ftype:
+     else {//unrecognized subfeatures
+       //make this GffObj of the same feature type
        ftype_id=names->feats.addName(gffline->ftype);
        }
-    if (gffline->gname!=NULL) {
-       gname=gffline->gname;
-       gffline->gname=NULL;
-       }
-    gseq_id=names->gseqs.addName(gffline->gseqname);
-    track_id=names->tracks.addName(gffline->track);
-    strand=gffline->strand;
-    qlen=gffline->qlen;
-    start=gffline->fstart;
-    end=gffline->fend;
-    isCDS=gffline->is_cds; //for now
-    if (gffline->ID==NULL) {
-        gffID=Gstrdup(gffline->Parent);
+    if (gffline->ID==NULL) { //typical GTF
+        gffID=Gstrdup(gffline->parents[0]);
+        this->createdByExon(true);
         //this is likely the first exon/segment of the feature
         addExon(gfrd, gffline, keepAttr, noExonAttr);
         }
-      else { //a parented feature with an ID -- could be an orphan GFF line
-        //just save the attributes but don't add itself as exon
-        gffID=Gstrdup(gffline->ID);
-        if (keepAttr) this->parseAttrs(attrs, gffline->info, noExonAttr);
+      else { //a parented feature with an ID -- probably an orphan GFF3 line
+        if (gffline->is_gff3 && gffline->exontype!=0) {
+             //premature exon given before its parent transcript
+             //create the transcript entry here
+             gffID=Gstrdup(gffline->parents[0]);
+             this->createdByExon(true);
+             //this is the first exon/segment of the transcript
+             addExon(gfrd, gffline, keepAttr, noExonAttr);
+             }
+            else { //unrecognized non-exon feature ? use the ID instead
+             gffID=Gstrdup(gffline->ID);
+             if (keepAttr) this->parseAttrs(attrs, gffline->info, noExonAttr);
+             }
         }
-    //this parses attrs and if noExonAttr is true attrs are
-    //assigned directly at transcript level
-  } //creation by direct subfeature
-  else { //gffline->Parent==NULL, and GffReader made sure of this if this line describes
-    //a parent feature in itself, even if it had a Parent= attribute in the text line
+    } //subfeature given directly
+  else { //gffline->parents==NULL
+    //create a parent feature in its own right
     gscore=gffline->score;
     if (gffline->ID==NULL || gffline->ID[0]==0)
       GError("Error: no ID found for GFF record start\n");
     gffID=Gstrdup(gffline->ID); //there must be an ID here
-    if (gffline->is_mrna) ftype_id=gff_fid_mRNA;
-    else ftype_id=names->feats.addName(gffline->ftype);
-    if (gffline->gname!=NULL) {
-      gname=gffline->gname;
-      gffline->gname=NULL;
-      }
-    start=gffline->fstart;
-    end=gffline->fend;
-    gseq_id=names->gseqs.addName(gffline->gseqname);
-    track_id=names->tracks.addName(gffline->track);
-    qlen=gffline->qlen;
-    qstart=gffline->qstart;
-    qend=gffline->qend;
-    strand=gffline->strand;
+    //if (gffline->is_transcript) ftype_id=gff_fid_mRNA;
+      //else
+    ftype_id=names->feats.addName(gffline->ftype);
+    if (gffline->is_transcript)
+      exon_ftype_id=gff_fid_exon;
+
     if (keepAttr) this->parseAttrs(attrs, gffline->info, noExonAttr);
-    }
+    }//no parent
+
+  if (gffline->gene_name!=NULL) {
+     gene_name=Gstrdup(gffline->gene_name);
+     }
+  if (gffline->gene_id!=NULL) {
+     geneID=Gstrdup(gffline->gene_id);
+     }
+
   GSeqStat* gsd=gfrd->gseqstats.AddIfNew(new GSeqStat(gseq_id,names->gseqs.lastNameUsed()),true);
   uptr=gsd;
   if (start<gsd->mincoord) gsd->mincoord=start;
@@ -628,7 +775,6 @@ GffObj::GffObj(GffReader *gfrd, GffLine* gffline, bool keepAttr, bool noExonAttr
 GffLine* GffReader::nextGffLine() {
  if (gffline!=NULL) return gffline; //caller should free gffline after processing
  while (gffline==NULL) {
-    //const char* l=linebuf->getLine();
     int llen=0;
     buflen=GFF_LINELEN-1;
     char* l=fgetline(linebuf, buflen, fh, &fpos, &llen);
@@ -642,7 +788,16 @@ GffLine* GffReader::nextGffLine() {
     if (gffline->skip) {
        delete gffline;
        gffline=NULL;
+       continue;
        }
+    if (gffline->ID==NULL && gffline->parents==NULL)  { //it must have an ID
+        //this might not be needed, already checked in the GffLine constructor
+        if (gff_warns)
+            GMessage("Warning: malformed GFF line, no parent or record Id (kipping\n");
+        delete gffline;
+        gffline=NULL;
+        //continue;
+        }
     }
 return gffline;
 }
@@ -680,7 +835,7 @@ GfoHolder* GffReader::gfoFind(const char* id, const char* ctg) {
  return r;
 }
 
-GfoHolder* GffReader::newGffRec(GffLine* gffline, bool keepAttr, bool noExonAttr, int replaceidx) {
+GfoHolder* GffReader::replaceGffRec(GffLine* gffline, bool keepAttr, bool noExonAttr, int replaceidx) {
   GffObj* newgfo=new GffObj(this, gffline, keepAttr, noExonAttr);
   GfoHolder* r=NULL;
   if (replaceidx>=0) {
@@ -694,7 +849,7 @@ GfoHolder* GffReader::newGffRec(GffLine* gffline, bool keepAttr, bool noExonAttr
   if (gff_warns) {
     int* pcount=tids.Find(newgfo->gffID);
     if (pcount!=NULL) {
-       GMessage("Warning: duplicate GFF ID: %s\n", newgfo->gffID);
+       if (gff_warns) GMessage("Warning: duplicate GFF ID: %s\n", newgfo->gffID);
        (*pcount)++;
        }
      else {
@@ -704,12 +859,68 @@ GfoHolder* GffReader::newGffRec(GffLine* gffline, bool keepAttr, bool noExonAttr
   return r;
 }
 
-bool GffReader::addSubFeature(GfoHolder* prevgfo, GffLine* gffline, GHash<CNonExon>& pex, bool noExonAttr) {
+GfoHolder* GffReader::updateParent(GfoHolder* newgfh, GffObj* parent) {
+  //assert(parent);
+  //assert(newgfo);
+  parent->children.Add(newgfh->gffobj);
+  if (newgfh->gffobj->parent==NULL) newgfh->gffobj->parent=parent;
+  newgfh->gffobj->setLevel(parent->getLevel()+1);
+  if (parent->isGene()) {
+      if (parent->gene_name!=NULL && newgfh->gffobj->gene_name==NULL)
+        newgfh->gffobj->gene_name=Gstrdup(parent->gene_name);
+      if (parent->geneID!=NULL && newgfh->gffobj->geneID==NULL)
+        newgfh->gffobj->geneID=Gstrdup(parent->geneID);
+      }
+
+  return newgfh;
+}
+
+GfoHolder* GffReader::newGffRec(GffLine* gffline, bool keepAttr, bool noExonAttr,
+                          GffObj* parent, GffExon* pexon) {
+  GffObj* newgfo=new GffObj(this, gffline, keepAttr, noExonAttr);
+  GfoHolder* r=NULL;
+  int gfoidx=gflst.Add(newgfo);
+  r=gfoAdd(newgfo->gffID, gffline->gseqname, newgfo, gfoidx);
+  if (parent!=NULL) {
+    updateParent(r, parent);
+    if (pexon!=NULL) parent->removeExon(pexon);
+    }
+  if (gff_warns) {
+    int* pcount=tids.Find(newgfo->gffID);
+    if (pcount!=NULL) {
+       if (gff_warns) GMessage("Warning: duplicate GFF ID: %s\n", newgfo->gffID);
+       (*pcount)++;
+       }
+     else {
+       tids.Add(newgfo->gffID,new int(1));
+       }
+    }
+  return r;
+}
+
+GfoHolder* GffReader::updateGffRec(GfoHolder* prevgfo, GffLine* gffline, 
+                                         bool keepAttr, bool noExonAttr) {
+ if (prevgfo==NULL) return NULL;
+ prevgfo->gffobj->createdByExon(false);
+ prevgfo->gffobj->ftype_id=prevgfo->gffobj->names->feats.addName(gffline->ftype);
+ prevgfo->gffobj->start=gffline->fstart;
+ prevgfo->gffobj->end=gffline->fend;
+ prevgfo->gffobj->isGene(gffline->is_gene);
+ prevgfo->gffobj->isTranscript(gffline->is_transcript || gffline->exontype!=0);
+ prevgfo->gffobj->fromGff3(gffline->is_gff3);
+ if (keepAttr) {
+   if (prevgfo->gffobj->attrs!=NULL) prevgfo->gffobj->attrs->Clear();
+   prevgfo->gffobj->parseAttrs(prevgfo->gffobj->attrs, gffline->info, noExonAttr);
+   }
+ return prevgfo;
+}
+
+
+bool GffReader::addExonFeature(GfoHolder* prevgfo, GffLine* gffline, GHash<CNonExon>& pex, bool noExonAttr) {
   bool r=true;
   if (gffline->strand!=prevgfo->gffobj->strand) {
-     GMessage("Error: duplicate GFF ID '%s' (exons found on different strands of %s)\n",
+     GMessage("GFF Error: duplicate GFF ID '%s' (exons found on different strands of %s)\n",
         prevgfo->gffobj->gffID, prevgfo->gffobj->getGSeqName());
-     //validation_errors = true;
       r=false;
      }
   int gdist=(gffline->fstart>prevgfo->gffobj->end) ? gffline->fstart-prevgfo->gffobj->end :
@@ -722,101 +933,151 @@ bool GffReader::addSubFeature(GfoHolder* prevgfo, GffLine* gffline, GHash<CNonEx
     if (!gff_warns) exit(1);
     }
   int eidx=prevgfo->gffobj->addExon(this, gffline, !noExonAttr, noExonAttr);
-  if (eidx>=0 && gffline->ID!=NULL && gffline->exontype==0) {
-     //this might become a parent later
-     char* xbuf=gfoBuildId(gffline->ID, gffline->gseqname);
-     pex.Add(xbuf, new CNonExon(prevgfo->idx, prevgfo->gffobj,
-            prevgfo->gffobj->exons[eidx], gffline));
-     GFREE(xbuf);
-     }
-   return r;
+  if (eidx>=0 && gffline->ID!=NULL && gffline->exontype==0)
+      subfPoolAdd(pex, prevgfo);
+  return r;
+}
+
+CNonExon* GffReader::subfPoolCheck(GffLine* gffline, GHash<CNonExon>& pex, char*& subp_name) {
+  CNonExon* subp=NULL;
+  subp_name=NULL;
+  for (int i=0;i<gffline->num_parents;i++) {
+    if (transcriptsOnly && discarded_ids.Find(gffline->parents[i])!=NULL)
+        continue;
+    subp_name=gfoBuildId(gffline->parents[i], gffline->gseqname); //e.g. mRNA name
+    subp=pex.Find(subp_name);
+    if (subp!=NULL)
+       return subp;
+    GFREE(subp_name);
+    }
+  return NULL;
+}
+
+void GffReader::subfPoolAdd(GHash<CNonExon>& pex, GfoHolder* newgfo) {
+//this might become a parent feature later
+if (newgfo->gffobj->exons.Count()>0) {
+   char* xbuf=gfoBuildId(gffline->ID, gffline->gseqname);
+   pex.Add(xbuf, new CNonExon(newgfo->idx, newgfo->gffobj,
+       newgfo->gffobj->exons[0], gffline));
+   GFREE(xbuf);
+   }
 }
 
-//this should be safe to use -- instead of all the parse* functions
+GfoHolder* GffReader::promoteFeature(CNonExon* subp, char*& subp_name, GHash<CNonExon>& pex,
+    bool keepAttr, bool noExonAttr) {
+  GffObj* prevp=subp->parent; //grandparent of gffline (e.g. gene)
+  if (prevp!=gflst[subp->idx])
+    GError("Error promoting subfeature %s, gflst index mismatch?!\n", subp->gffline->ID);
+  subp->gffline->discardParent();
+  GfoHolder* gfoh=newGffRec(subp->gffline, keepAttr, noExonAttr, prevp, subp->exon);
+  pex.Remove(subp_name); //no longer a potential parent, moved it to phash already
+  prevp->promotedChildren(true);
+  return gfoh; //returns the holder of newly promoted feature
+}
+
+//have to parse the whole file because exons can be scattered all over
 void GffReader::readAll(bool keepAttr, bool mergeCloseExons, bool noExonAttr) {
-    bool validation_errors = false;
-    //loc_debug=false;
-    GHash<CNonExon> pex;
-    while (nextGffLine()!=NULL) {
-    if (gffline->Parent==NULL) {//no parent, new GFF3-like record starting
-        if (gffline->ID == NULL)  {
-            GMessage("Warning: malformed GFF line encountered, no  record Id.  Skipping..\n");
-            delete gffline;
-            gffline=NULL;
-            continue;
-            }        
-        //check for uniqueness of gffline->ID in phash !        
-       GfoHolder* f=gfoFind(gffline->ID, gffline->gseqname);
-       if (f!=NULL) {
-            GMessage("Error: duplicate GFF ID '%s' encountered!\n",gffline->ID);
-            validation_errors = true;
-            if (gff_warns) { delete gffline; gffline=NULL; continue; }
-                      else exit(1);
+  bool validation_errors = false;
+  //loc_debug=false;
+  GHash<CNonExon> pex; //keep track of any "exon"-like features that have an ID
+                     //and thus could become promoted to parent features
+  while (nextGffLine()!=NULL) {
+       //seen this gff ID before?
+     GfoHolder* prevseen=NULL;
+     if (gffline->ID) //GFF3
+         prevseen=gfoFind(gffline->ID, gffline->gseqname);
+     if (prevseen!=NULL) {
+            if (prevseen->gffobj->createdByExon()) {
+                updateGffRec(prevseen, gffline, keepAttr, noExonAttr);
+                }
+             else {
+                GMessage("Error: duplicate GFF ID '%s' encountered!\n",gffline->ID);
+                validation_errors = true;
+                if (gff_warns) { 
+                       delete gffline; gffline=NULL; continue;
+                       }
+                   else exit(1);
+                }
             }
-       this->newGffRec(gffline, keepAttr, noExonAttr);
+    if (gffline->parents==NULL) {//start GFF3-like record with no parent (mRNA, gene)
+       if (!prevseen) newGffRec(gffline, keepAttr, noExonAttr);
        }
-    else { //--- it's a parented subfeature (exon/CDS/other):
-       GfoHolder* prevgfo=gfoFind(gffline->Parent, gffline->gseqname);
-       if (prevgfo!=NULL) { //exon of a previously seen GffObj
-                 if (!addSubFeature(prevgfo, gffline, pex, noExonAttr))
-                     validation_errors=true;
-                 }
-            else {//new GTF-like record starting here with a subfeature directly
-               //check if this subfeature isn't parented by another subfeature
-               char* xbuf=gfoBuildId(gffline->Parent, gffline->gseqname);
-               CNonExon* subp=pex.Find(xbuf);
-               GFREE(xbuf);
-               if (subp!=NULL) {
-                 //promote this subp "exon" as a full GffObj
-                 GffObj* prevp=subp->parent;
-                 if (prevp!=gflst[subp->idx])
-                   GError("Error promoting subfeature %s, gflst index mismatch?!\n", subp->gffline->ID);
-                 xbuf=gfoBuildId(subp->gffline->Parent, subp->gffline->gseqname);
-                 subp->gffline->discardParent();
-                 GfoHolder* gfoh=newGffRec(subp->gffline, keepAttr, noExonAttr, subp->idx);
-                 phash.Remove(xbuf); //gfoRemove()
-                 pex.Remove(xbuf);
-                 GFREE(xbuf);
-                 delete prevp;
-                 if (!this->addSubFeature(gfoh, gffline, pex, noExonAttr))
-                     validation_errors=true;
-                 }
-                else { //parented feature without parent line
-                 //loc_debug=true;
-                 GfoHolder* newgfo=this->newGffRec(gffline, keepAttr, noExonAttr);
-                 if (gffline->ID!=NULL && gffline->exontype==0) { 
-                   //this might become a parent feature later
-                   if (newgfo->gffobj->exons.Count()>0) {
-                      char* xbuf=gfoBuildId(gffline->ID, gffline->gseqname);
-                      pex.Add(xbuf, new CNonExon(newgfo->idx, newgfo->gffobj,
-                          newgfo->gffobj->exons[0], gffline));
-                      GFREE(xbuf);
-                      }
-                    }
-                 //even those with errors will be added here!
-                 }
-              }
-       } //subfeature
-      //--
-    delete gffline;
-    gffline=NULL;
-    }//while
+    else { //--- it's a parented feature (could still be a mRNA)
+       bool found_parent=false;
+       GfoHolder* newgfo=prevseen;
+       for (int i=0;i<gffline->num_parents;i++) {
+            if (transcriptsOnly && discarded_ids.Find(gffline->parents[i])!=NULL)
+                continue; //skipping discarded parent feature
+            GfoHolder* parentgfo=gfoFind(gffline->parents[i], gffline->gseqname);
+            if (parentgfo!=NULL) { //parent GffObj parsed earlier
+                   found_parent=true;
+                   if (parentgfo->gffobj->isGene() && gffline->is_transcript
+                                   && gffline->exontype==0) {
+                       //not an exon, but a transcript parented by a gene
+                       if (newgfo) {
+                           updateParent(newgfo, parentgfo->gffobj);
+                           }
+                         else {
+                           newgfo=newGffRec(gffline, keepAttr, noExonAttr, parentgfo->gffobj);
+                           }
+                       }
+                   else { //potential exon subfeature
+                       if (!addExonFeature(parentgfo, gffline, pex, noExonAttr))
+                         validation_errors=true;
+                       }
+                   }
+            } //for each parsed parent Id
+       if (!found_parent) { //new GTF-like record starting here with a subfeature directly
+             //or it could be some chado GFF3 barf with exons declared BEFORE their parent :(
+            //check if this feature isn't parented by a previously stored "exon" subfeature
+            char* subp_name=NULL;
+            CNonExon* subp=subfPoolCheck(gffline, pex, subp_name);
+            if (subp!=NULL) { //found a subfeature that is the parent of this gffline
+               //promote that subfeature to a full GffObj
+               GfoHolder* gfoh=promoteFeature(subp, subp_name, pex, keepAttr, noExonAttr);
+               //add current gffline as an exon of the newly promoted subfeature
+               if (!addExonFeature(gfoh, gffline, pex, noExonAttr))
+                      validation_errors=true;
+               }
+              else { //no parent seen before, create one directly with this exon
+               //loc_debug=true;
+               GfoHolder* newgfo=prevseen ? prevseen : newGffRec(gffline, keepAttr, noExonAttr);
+               if (gffline->ID!=NULL && gffline->exontype==0)
+                     subfPoolAdd(pex, newgfo);
+               //even those with errors will be added here!
+               }
+            GFREE(subp_name);
+            } //no previous parent found
+       } //parented feature
+        //--
+      delete gffline;
+      gffline=NULL;
+      }//while gff lines
   gflst.finalize(this, mergeCloseExons); //force sorting by locus if so constructed
  // all gff records are now loaded in GList gflst
  // so we can free the hash
   phash.Clear();
   tids.Clear();
   if (validation_errors) {
-        exit(1);
+    exit(1);
     }
 }
 
 GffObj* GffObj::finalize(GffReader* gfr, bool mergeCloseExons) {
- //merge adjacent or overlapping segments anyway
+ //merge
+ //always merge adjacent or overlapping segments
  //but if mergeCloseExons then merge even when distance is up to 5 bases
  udata=0;
  uptr=NULL;
- if (ftype_id==gff_fid_mRNA) {
+ if (gfr->transcriptsOnly && !(isTranscript() || (isGene() && children.Count()==0))) {
+       isDiscarded(true);
+       }
+ if (ftype_id==gff_fid_transcript && CDstart>0) {
+    ftype_id=gff_fid_mRNA;
+    //exon_ftype_id=gff_fid_exon;
+    }
+ //if (ftype_id==gff_fid_mRNA || exon_ftype_id==gff_fid_exon || mergeCloseExons) {
+ if (isTranscript() || exon_ftype_id==gff_fid_exon || mergeCloseExons) {
    int mindist=mergeCloseExons ? 5:1;
    for (int i=0;i<exons.Count()-1;i++) {
      int ni=i+1;
@@ -824,9 +1085,7 @@ GffObj* GffObj::finalize(GffReader* gfr, bool mergeCloseExons) {
      while (ni<exons.Count()) {
        int dist=(int)(exons[ni]->start-mend);
        if (dist>mindist) break; //no merging with next segment
-       //if (dist<0 || dist>mindist) break;
-       //if (dist<=1 && gfr->gff_warns) { //overlapping or adjacent exons
-       if (gfr!=NULL && gfr->gff_warns) {
+       if (gfr!=NULL && gfr->gff_warns && dist!=0 && (exons[ni]->exontype!=exgffUTR && exons[i]->exontype!=exgffUTR)) {
           GMessage("GFF warning: merging adjacent/overlapping segments of %s on %s (%d-%d, %d-%d)\n",
                gffID, getGSeqName(), exons[i]->start, exons[i]->end,exons[ni]->start, exons[ni]->end);
           }
@@ -869,9 +1128,11 @@ void GffObj::parseAttrs(GffAttrs*& atrlist, char* info, bool noExonAttr) {
     char* ech=strchr(start,'=');
     if (ech!=NULL) { // attr=value format found
        *ech='\0';
-       if (strcmp(start, "ID")==0 || strcmp(start,"Name")==0 || strcmp(start,"Parent")==0 ||
-        strcmp(start,"transcript_id")==0 || strcmp(start,"gene_id")==0)
-            { start=pch; continue; } //skip this already recognized and stored attribute
+       /*
+       if (strcmp(start, "Target")==0 || strcmp(start,"Parent")==0 ||
+           strcmp(start,"transcript_id")==0 || strcmp(start,"gene_id")==0)
+            { start=pch; continue; } //skip these already parsed attributes
+       */
        if (noExonAttr && (strcmp(start, "exon_number")==0 || strcmp(start, "exon")==0)) { start=pch; continue; }
        ech++;
        while (*ech==' ' && ech<endinfo) ech++;//skip extra spaces after the '='
@@ -957,13 +1218,11 @@ void GffObj::mRNA_CDS_coords(uint& cds_mstart, uint& cds_mend) {
              //CDstart in this segment
              //and we are getting the whole transcript
              cds_mend=s-(int)(CDstart-sgstart);
-             //GMessage("Setting cds_mend to %d\n",cds_mend);
              }
        if (CDend>=sgstart && CDend<=sgend) {
              //CDstart in this segment
              //and we are getting the whole transcript
              cds_mstart=s-(int)(CDend-cdsadj-sgstart);
-             //GMessage("Setting cds_mstart to %d\n",cds_mstart);
              }
       } //for each exon
     } // - strand
@@ -1251,8 +1510,7 @@ void GffObj::printGxfLine(FILE* fout, char* tlabel, char* gseqname, bool iscds,
      if (exons[exidx]->score) sprintf(scorestr,"%.2f", exons[exidx]->score);
      xattrs=exons[exidx]->attrs;
   }
-  char* geneid=(gname!=NULL)? gname : gffID;
-  if (phase==0) phase='.';
+  if (phase==0 || !iscds) phase='.';
   const char* ftype=iscds ? "CDS" : getSubfName();
   if (gff3) {
     fprintf(fout,
@@ -1266,20 +1524,29 @@ void GffObj::printGxfLine(FILE* fout, char* tlabel, char* gseqname, bool iscds,
          }
     fprintf(fout, "\n");
     } //GFF
-  else {//for GTF -- we can only print mRNAs here
-    fprintf(fout, "%s\t%s\t%s\t%d\t%d\t%s\t%c\t%c\t",
-        gseqname, tlabel, ftype, segstart, segend, scorestr, strand, phase);
-    if (ismRNA())
-       fprintf(fout,"gene_id \"%s\"; transcript_id \"%s\";", geneid, gffID);
+  else {//for GTF -- we print only transcripts
+    //if (isValidTranscript())
+    fprintf(fout, "%s\t%s\t%s\t%d\t%d\t%s\t%c\t%c\ttranscript_id \"%s\";",
+           gseqname, tlabel, ftype, segstart, segend, scorestr, strand, phase, gffID);
+    //char* geneid=(geneID!=NULL)? geneID : gffID;
+    if (geneID)
+      fprintf(fout," gene_id \"%s\";",geneID);
+    if (gene_name!=NULL) {
+       //fprintf(fout, " gene_name ");
+       //if (gene_name[0]=='"') fprintf (fout, "%s;",gene_name);
+       //              else fprintf(fout, "\"%s\";",gene_name);
+       fprintf(fout," gene_name \"%s\";",gene_name);
+       }
     if (xattrs!=NULL) {
-       for (int i=0;i<xattrs->Count();i++) {
-         if (xattrs->Get(i)->attr_val==NULL) continue;
-         fprintf(fout, " %s ",names->attrs.getName(xattrs->Get(i)->attr_id));
-          if (xattrs->Get(i)->attr_val[0]=='"')
-                  fprintf(fout, "%s;",xattrs->Get(i)->attr_val);
-             else fprintf(fout, "\"%s\";",xattrs->Get(i)->attr_val);
+          for (int i=0;i<xattrs->Count();i++) {
+            if (xattrs->Get(i)->attr_val==NULL) continue;
+            const char* attrname=names->attrs.getName(xattrs->Get(i)->attr_id);
+            fprintf(fout, " %s ",attrname);
+            if (xattrs->Get(i)->attr_val[0]=='"')
+                     fprintf(fout, "%s;",xattrs->Get(i)->attr_val);
+                else fprintf(fout, "\"%s\";",xattrs->Get(i)->attr_val);
+             }
           }
-       }
     fprintf(fout, "\n");
     }//GTF
 }
@@ -1290,9 +1557,8 @@ void GffObj::printGxf(FILE* fout, GffPrintMode gffp, char* tlabel) {
     tlabel=track_id>=0 ? names->tracks.Get(track_id)->name :
          (char*)"gffobj" ;
     }
-
  unxcoord();
- if (exons.Count()==0) return;
+ //if (exons.Count()==0) return;
  char* gseqname=names->gseqs.Get(gseq_id)->name;
  bool gff3 = (gffp>=pgffAny);
  bool showCDS = (gffp==pgtfAny || gffp==pgtfCDS || gffp==pgffCDS || gffp==pgffAny || gffp==pgffBoth);
@@ -1307,16 +1573,22 @@ void GffObj::printGxf(FILE* fout, GffPrintMode gffp, char* tlabel) {
       pend=CDend;
       }
    else { pstart=start;pend=end; }
-   const char* ftype=ismRNA() ? "mRNA" : getFeatureName();
+   //const char* ftype=isTranscript() ? "mRNA" : getFeatureName();
+   const char* ftype=getFeatureName();
    fprintf(fout,
      "%s\t%s\t%s\t%d\t%d\t%s\t%c\t.\tID=%s",
      gseqname, tlabel, ftype, pstart, pend, tmpstr, strand, gffID);
-   if (gname!=NULL)
-       fprintf(fout, ";Name=%s",gname);
-   if (CDstart>0 && !showCDS && !isCDS) fprintf(fout,";CDS=%d:%d",CDstart,CDend);
+   if (CDstart>0 && !showCDS && !isCDS) fprintf(fout,";CDS=%d-%d",CDstart,CDend);
+   if (parent!=NULL && !parent->isDiscarded())
+       fprintf(fout, ";Parent=%s",parent->getID());
+   if (geneID!=NULL)
+      fprintf(fout, ";geneID=%s",geneID);
+   if (gene_name!=NULL)
+      fprintf(fout, ";gene_name=%s",gene_name);
    if (attrs!=NULL) {
       for (int i=0;i<attrs->Count();i++) {
-        fprintf(fout,";%s=%s", names->attrs.getName(attrs->Get(i)->attr_id),
+        const char* attrname=names->attrs.getName(attrs->Get(i)->attr_id);
+        fprintf(fout,";%s=%s", attrname,
                attrs->Get(i)->attr_val);
         }
       }
@@ -1405,38 +1677,3 @@ void GffObj::getCDSegs(GArray<GffCDSeg>& cds) {
        } //for each exon
    } // + strand
 }
-/*
-#ifdef DEBUG
-void GffObj::dbgPrint(const char* msg) {
- if (msg!=NULL) fprintf(stdout, ">> %s\n",msg);
- char* tlabel=track_id>=0 ? names->tracks.Get(track_id)->name :
-       (char*)"gmapobj" ;
- char scorestr[14];
- char strand=revstrand?'-':'+';
- unxcoord();
- char* gseqname=names->gseqs.Get(gseq_id)->name;
- char* fname=f_id>=0 ? names->feats.Get(f_id)->name : (char*)"nofeatname";
-
- fprintf(stdout, "%s\t%s\t%s\t%d\t%d\t.\t%c\t.\tID=%s;Name=%s\n",
-       gseqname, tlabel, fname, start, end, strand, gffID, gffID);
-
- for (int fi=0;fi<features->Count();fi++) {
-   GFeature* feature=features->Get(fi);
-   fname=names->feats.Get(feature->name_id)->name;
-   GffExon* segs=feature->segs;
-   int segcount=feature->segcount;
-   if (segcount==0 || segs==NULL) continue;
-   for (int i=0;i<segcount;i++) {
-      if (segs[i].start==0) continue;
-      if (segs[i].score) sprintf(scorestr,"%.2f", segs[i].score/100.00);
-                  else strcpy(scorestr,".");
-      fprintf(stdout,
-         "%s\t%s\t%s\t%d\t%d\t%s\t%c\t.\tParent=%s\n",
-         gseqname, tlabel, fname, segs[i].start, segs[i].end, scorestr, strand, gffID);
-      }
-   }
- fflush(stdout);
-}
-#endif
-*/
-
diff --git a/src/gff.h b/src/gff.h
index 454cd63..985c609 100644
--- a/src/gff.h
+++ b/src/gff.h
@@ -17,34 +17,53 @@ const byte exMskTag = 0x80;
 */
 
 //reserved Gffnames::feats entries -- basic feature types
-extern const int gff_fid_mRNA;
+extern const int gff_fid_mRNA; // "mRNA" feature name
+extern const int gff_fid_transcript; // *RNA, *transcript feature name
 extern const int gff_fid_exon;
 extern const int gff_fid_CDS; //never really used, except for display only
                               //use gff_fid_exon instead
-//extern bool gff_warns; //show parser warnings - now GffReader member
-
 extern const uint GFF_MAX_LOCUS;
 extern const uint GFF_MAX_EXON;
 extern const uint GFF_MAX_INTRON;
 
+extern const uint gfo_flag_CHILDREN_PROMOTED;
+extern const uint gfo_flag_HAS_ERRORS;
+extern const uint gfo_flag_IS_GENE;
+extern const uint gfo_flag_FROM_GFF3; //parsed from GFF3 formatted record
+extern const uint gfo_flag_BY_EXON;  //created by subfeature (exon) directly
+                      //(GTF2 and some chado gff3 dumps with exons given before their mRNA)
+extern const uint gfo_flag_IS_TRANSCRIPT; //recognized as '*RNA' or '*transcript'
+extern const uint gfo_flag_DISCARDED; //should not be printed under the "transcriptsOnly" directive
+extern const uint gfo_flag_LST_KEEP; //GffObj from GffReader::gflst is to be kept (not deallocated)
+                                     //when GffReader is destroyed
+extern const uint gfo_flag_LEVEL_MSK; //hierarchical level: 0 = no parent
+extern const byte gfo_flagShift_LEVEL;
+
+extern bool gff_show_warnings;
+
 #define GFF_LINELEN 2048
 #define ERR_NULL_GFNAMES "Error: GffObj::%s requires a non-null GffNames* names!\n"
 
 
 enum GffExonType {
-  exgffNone=0,
+  exgffNone=0,  //not a recognizable exon or CDS segment
   exgffStart, //from "start_codon" feature (within CDS)
   exgffStop, //from "stop_codon" feature (may be outside CDS)
   exgffCDS,  //from "CDS" feature
   exgffUTR,  //from "UTR" feature
+  exgffCDSUTR, //from a merge of UTR and CDS feature
   exgffExon, //from "exon" feature
 };
 
 class GffReader;
 
 class GffLine {
+    char* _parents; //stores a copy of the Parent attribute value,
+       //with commas replaced by \0
+    int _parents_len;
  public:
-    char* line;
+    char* dupline; //duplicate of original line
+    char* line; //this will have tabs replaced by \0
     int llen;
     char* gseqname;
     char* track;
@@ -58,59 +77,92 @@ class GffLine {
     double score;
     char strand;
     bool skip;
+    bool is_gff3; //if the line appears to be in GFF3 format
     bool is_cds; //"cds" and "stop_codon" features
     bool is_exon; //"exon" and "utr" features
     char exontype; // gffExonType
-    bool is_mrna;
+    bool is_transcript; //if current feature is *RNA or *transcript
+    bool is_gene; //if current feature is *gene
     char phase;  // '.' , '0', '1' or '2'
     // -- allocated strings:
-    char* gname; //gene_id or Name= value (or an otherwise parsed gene denominator
-                 //(for grouping isoforms)
+    char* gene_name; //value of gene_name attribute (GTF) if present or Name attribute of a gene feature (GFF3)
+    char* gene_id; //value of gene_id attribute (GTF) if present or ID attribute of a gene feature (GFF3)
     //
-    char* Parent; // if a Parent=.. attribute was found
+    char** parents; //for GTF only parents[0] is used
+    int num_parents;
     char* ID;     // if a ID=.. attribute was parsed, or a GTF with 'transcript' line (transcript_id)
     GffLine(GffReader* reader, const char* l); //parse the line accordingly
-    void discardParent() { GFREE(Parent); }
+    void discardParent() {
+       GFREE(_parents);
+       _parents_len=0;
+       num_parents=0;
+       parents=NULL;
+       }
+    char* extractAttr(const char* pre, bool caseStrict=true, bool enforce_GTF2=false);
     GffLine(GffLine* l) { //a copy constructor
       memcpy((void*)this, (void*)l, sizeof(GffLine));
       line=NULL;
       GMALLOC(line, llen+1);
-      memcpy((void*)line, (void*)(l->line), llen+1);
-      //--offsets withing line[]
+      memcpy(line, l->line, llen+1);
+      GMALLOC(dupline, llen+1);
+      memcpy(dupline, l->dupline, llen+1);
+      //--offsets within line[]
       gseqname=line+(l->gseqname-l->line);
       track=line+(l->track-l->line);
       ftype=line+(l->ftype-l->line);
       info=line+(l->info-l->line);
+      //Parent=Gstrdup(l->Parent);
+      if (l->_parents_len>0) {
+         _parents_len=l->_parents_len;
+         GMALLOC(_parents, _parents_len);
+         memcpy(_parents, l->_parents, _parents_len);
+         num_parents=l->num_parents;
+         for (int i=0;i<num_parents;i++) {
+            parents[i]=_parents+(l->parents[i] - l->_parents);
+            }
+         }
       //-- allocated string copies:
-      Parent=Gstrdup(l->Parent);
-      ID=Gstrdup(ID);
-      gname=Gstrdup(gname);
+      ID=Gstrdup(l->ID);
+      if (l->gene_name!=NULL)
+          gene_name=Gstrdup(l->gene_name);
+      if (l->gene_id!=NULL)
+          gene_id=Gstrdup(l->gene_id);
       }
     GffLine() {
       line=NULL;
+      dupline=NULL;
       gseqname=NULL;
       track=NULL;
       ftype=NULL;
       fstart=0;
       fend=0;
       info=NULL;
-      Parent=NULL;
+      _parents=NULL;
+      _parents_len=0;
+      parents=NULL;
+      num_parents=0;
       ID=NULL;
-      gname=NULL;
+      gene_name=NULL;
+      gene_id=NULL;
       skip=true;
       qstart=0;
       qend=0;
       qlen=0;
       exontype=0;
       is_cds=false;
-      is_mrna=false;
+      is_gff3=false;
+      is_transcript=false;
+      is_gene=false;
       is_exon=false;
       }
     ~GffLine() {
+      GFREE(dupline);
       GFREE(line);
-      GFREE(Parent);
+      GFREE(_parents);
+      GFREE(parents);
       GFREE(ID);
-      GFREE(gname);
+      GFREE(gene_name);
+      GFREE(gene_id);
      }
 };
 
@@ -237,12 +289,13 @@ class GffNames {
    GffNameList tracks;
    GffNameList gseqs;
    GffNameList attrs;
-   GffNameList feats; //feature names - anything except 'mRNA', 'exon', 'CDS' gets stored here
+   GffNameList feats; //feature names: 'mRNA', 'exon', 'CDS' etc.
    GffNames():tracks(),gseqs(),attrs(), feats() {
     numrefs=0;
     //the order below is critical!
     //has to match: gff_fid_mRNA, gff_fid_exon, gff_fid_CDS
     feats.addStatic("mRNA");//index 0=gff_fid_mRNA
+    feats.addStatic("transcript");//index 1=gff_fid_transcript
     feats.addStatic("exon");//index 1=gff_fid_exon
     feats.addStatic("CDS"); //index 2=gff_fid_CDS
     }
@@ -339,19 +392,22 @@ class GffObj:public GSeg {
             //'-' : (start,end) are relative to the reverse complement of xstart..xend region
    //--
    char* gffID; // ID name for mRNA (parent) feature
-   char* gname; // value of Name or "gene_id" attribute (if given)
+   char* gene_name; //value of gene_name attribute (GTF) if present or Name attribute of the parent gene feature (GFF3)
+   char* geneID; //value of gene_id attribute (GTF) if present or ID attribute of a parent gene feature (GFF3)
+   unsigned int flags;
    //-- friends:
    friend class GffReader;
    friend class GffExon;
 public:
-  bool hasErrors; //overlapping exons, or too short introns, etc.
   static GffNames* names; // common string storage that holds the various attribute names etc.
   int track_id; // index of track name in names->tracks
   int gseq_id; // index of genomic sequence name in names->gseqs
   int ftype_id; // index of this record's feature name in names->feats, or the special gff_fid_mRNA value
-  int subftype_id; //index of child subfeature name in names->feats (that subfeature stored in "exons")
+  int exon_ftype_id; //index of child subfeature name in names->feats (that subfeature stored in "exons")
                    //if ftype_id==gff_fid_mRNA then this value is ignored
   GList<GffExon> exons; //for non-mRNA entries, these can be any subfeature of type subftype_id
+  GPVec<GffObj> children;
+  GffObj* parent;
   int udata; //user data, flags etc.
   void* uptr; //user pointer (to a parent object, cluster, locus etc.)
   GffObj* ulink; //link to another GffObj (user controlled field)
@@ -361,19 +417,79 @@ public:
   uint CDstart; //CDS start coord
   uint CDend;   //CDS end coord
   char CDphase; //initial phase for CDS start
+  bool hasErrors() { return ((flags & gfo_flag_HAS_ERRORS)!=0); }
+  void hasErrors(bool v) {
+      if (v) flags |= gfo_flag_HAS_ERRORS;
+        else flags &= ~gfo_flag_HAS_ERRORS;
+      }
+  bool fromGff3() { return ((flags & gfo_flag_FROM_GFF3)!=0); }
+  void fromGff3(bool v) {
+      if (v) flags |= gfo_flag_FROM_GFF3;
+        else flags &= ~gfo_flag_FROM_GFF3;
+      }
+  bool createdByExon() { return ((flags & gfo_flag_BY_EXON)!=0); }
+  void createdByExon(bool v) {
+      if (v) flags |= gfo_flag_BY_EXON;
+        else flags &= ~gfo_flag_BY_EXON;
+      }
+  bool isGene() { return ((flags & gfo_flag_IS_GENE)!=0); }
+  void isGene(bool v) {
+      if (v) flags |= gfo_flag_IS_GENE;
+        else flags &= ~gfo_flag_IS_GENE;
+      }
+  bool isDiscarded() { return ((flags & gfo_flag_DISCARDED)!=0); }
+  void isDiscarded(bool v) {
+      if (v) flags |= gfo_flag_DISCARDED;
+        else flags &= ~gfo_flag_DISCARDED;
+      }
+      
+  bool isUsed() { return ((flags & gfo_flag_LST_KEEP)!=0); }
+  void isUsed(bool v) {
+      if (v) flags |= gfo_flag_LST_KEEP;
+        else flags &= ~gfo_flag_LST_KEEP;
+      }
+  bool isTranscript() { return ((flags & gfo_flag_IS_TRANSCRIPT)!=0); }
+  void isTranscript(bool v) {
+      if (v) flags |= gfo_flag_IS_TRANSCRIPT;
+        else flags &= ~gfo_flag_IS_TRANSCRIPT;
+      }
+  bool promotedChildren() { return ((flags & gfo_flag_CHILDREN_PROMOTED)!=0); }
+  void promotedChildren(bool v) {
+    if (v) flags |= gfo_flag_CHILDREN_PROMOTED;
+      else flags &= ~gfo_flag_CHILDREN_PROMOTED;
+     }
+  void setLevel(byte v) {
+    if (v==0) flags &= ~gfo_flag_LEVEL_MSK;
+         else flags &= ~(((uint)v) << gfo_flagShift_LEVEL);
+    }
+  byte incLevel() {
+    uint v=((flags & gfo_flag_LEVEL_MSK) >> gfo_flagShift_LEVEL);
+    v++;
+    flags &= ~(v << gfo_flagShift_LEVEL);
+    return v;
+    }
+  byte getLevel() {
+    return ((byte)((flags & gfo_flag_LEVEL_MSK) >> gfo_flagShift_LEVEL));
+    }
 
-  bool ismRNA() { return (ftype_id==gff_fid_mRNA); }
+  bool isValidTranscript() { 
+    //return (ftype_id==gff_fid_mRNA && exons.Count()>0);
+    return (isTranscript() && exons.Count()>0);
+    }
+  
 
   int addExon(uint segstart, uint segend, double sc=0, char fr='.',
              int qs=0, int qe=0, bool iscds=false, char exontype=0);
 
   int addExon(GffReader* reader, GffLine* gl, bool keepAttr=false, bool noExonAttr=true);
+
   void removeExon(int idx);
+  void removeExon(GffExon* p);
   char  strand; //true if features are on the reverse complement strand
   double gscore;
   double uscore; //custom, user-computed score, if needed
   int covlen; //total coverage of  reference genomic sequence (sum of maxcf segment lengths)
-
+  
    //--------- optional data:
   int qlen; //query length, start, end - if available
   int qstart;
@@ -391,14 +507,16 @@ public:
       if (sharedattrs) exons[0]->attrs=NULL;
       }
     }
-  GffObj(char* anid=NULL):GSeg(0,0), exons(true,true,false) { //exons: sorted, free, non-unique
+  GffObj(char* anid=NULL):GSeg(0,0), exons(true,true,false), children(1,false) {
+                                   //exons: sorted, free, non-unique
        gffID=NULL;
        uptr=NULL;
        ulink=NULL;
+       flags=0;
        udata=0;
-       hasErrors=false;
+       parent=NULL;
        ftype_id=-1;
-       subftype_id=-1;
+       exon_ftype_id=-1;
        if (anid!=NULL) gffID=Gstrdup(anid);
        gffnames_ref(names);
        qlen=0;
@@ -415,16 +533,18 @@ public:
        xstart=0;
        xend=0;
        xstatus=0;
-       strand=0;
+       strand='.';
        gscore=0;
        uscore=0;
        attrs=NULL;
        covlen=0;
-       gname=NULL;
+       gene_name=NULL;
+       geneID=NULL;
        }
    ~GffObj() {
        GFREE(gffID);
-       GFREE(gname);
+       GFREE(gene_name);
+       GFREE(geneID);
        clearAttrs();
        gffnames_unref(names);
        }
@@ -432,13 +552,13 @@ public:
    GffObj* finalize(GffReader* gfr, bool mergeCloseExons=false); //finalize parsing: must be called in order to merge adjacent/close proximity subfeatures
    void parseAttrs(GffAttrs*& atrlist, char* info, bool noExonAttr=false);
    const char* getSubfName() { //returns the generic feature type of the entries in exons array
-     int sid=subftype_id;
+     int sid=exon_ftype_id;
      if (sid==gff_fid_exon && isCDS) sid=gff_fid_CDS;
      return names->feats.getName(sid);
      }
    bool monoFeature() {
      return (exons.Count()==0 || 
-          (exons.Count()==1 && subftype_id==ftype_id));
+          (exons.Count()==1 && exon_ftype_id==ftype_id));
      }
    const char* getFeatureName() {
      return names->feats.getName(ftype_id);
@@ -506,14 +626,16 @@ public:
       }
     
     int exonOverlapIdx(uint s, uint e, int* ovlen=NULL) {
-      //return the exons' index for the overlapping exon
+      //return the exons' index for the overlapping OR ADJACENT exon
       //ovlen, if given, will return the overlap length
       if (s>e) swap(s,e);
+      s--;e++; //to also catch adjacent exons
       for (int i=0;i<exons.Count();i++) {
             if (exons[i]->start>e) break;
             if (s>exons[i]->end) continue;
             //-- overlap if we are here:
             if (ovlen!=NULL) {
+              s++;e--;
               int ovlend= (exons[i]->end>e) ? e : exons[i]->end;
               *ovlen= ovlend - ((s>exons[i]->start)? s : exons[i]->start)+1;
               }
@@ -633,19 +755,32 @@ public:
    bool operator>(GffObj& d){
       if (gseq_id!=d.gseq_id) return (gseq_id>d.gseq_id);
       if (start==d.start) {
-         if (end==d.end) return strcmp(gffID, d.gffID)>0;
-                      else return end>d.end;
+         if (getLevel()==d.getLevel()) {
+             if (end==d.end) return (strcmp(gffID, d.gffID)>0);
+                        else return (end>d.end);
+             } else return (getLevel()>d.getLevel());
          } else return (start>d.start);
       }
    bool operator<(GffObj& d){
      if (gseq_id!=d.gseq_id) return (gseq_id<d.gseq_id);
      if (start==d.start) {
-        if (end==d.end) return strcmp(gffID, d.gffID)<0;
+         if (getLevel()==d.getLevel()) {
+            if (end==d.end) return strcmp(gffID, d.gffID)<0;
                      else return end<d.end;
+            } else return (getLevel()<d.getLevel());
         } else return (start<d.start);
      }
    char* getID() { return gffID; }
-   char* getGene() { return gname; }
+   char* getGeneID() { return geneID; }
+   char* getGeneName() { return gene_name; }
+   void setGeneName(const char* gname) {
+        GFREE(gene_name);
+        if (gname) gene_name=Gstrdup(gname);
+        }
+   void setGeneID(const char* gene_id) {
+        GFREE(geneID);
+        if (gene_id) geneID=Gstrdup(gene_id);
+        }
    int addSeg(GffLine* gfline);
    int addSeg(int fnid, GffLine* gfline);
    void getCDSegs(GArray<GffCDSeg>& cds);
@@ -661,8 +796,8 @@ public:
    void printGff(FILE* fout, char* tlabel=NULL) {
       printGxf(fout, pgffAny, tlabel);
       }
-   void print_mRNAGff(FILE* fout, char* tlabel=NULL, bool showCDS=false) {
-      if (ismRNA())
+   void printTranscriptGff(FILE* fout, char* tlabel=NULL, bool showCDS=false) {
+      if (isValidTranscript())
          printGxf(fout, showCDS ? pgffBoth : pgffExon, tlabel);
       }
    void printSummary(FILE* fout=NULL);
@@ -695,9 +830,7 @@ class GSeqStat {
    uint maxcoord;
    uint maxfeat_len; //maximum feature length on this genomic sequence
    GffObj* maxfeat; 
-   //GList<GffObj> gflst;
    GSeqStat(int id=-1, char* name=NULL) {
-        //:gflst(true,false,false) {
      gseqid=id;
      gseqname=name;
      mincoord=MAXUINT;
@@ -720,22 +853,53 @@ class GSeqStat {
 int gfo_cmpByLoc(const pointer p1, const pointer p2);
 
 class GfList: public GList<GffObj> {
- //just adding the option to sort by genomic sequence and coordinate
+  //just adding the option to sort by genomic sequence and coordinate
    bool mustSort;
  public:
    GfList(bool sortbyloc=false):GList<GffObj>(false,false,false) {
-    mustSort=sortbyloc;
-    }
+     //GffObjs in this list are NOT deleted when the list is cleared
+     //-- for deallocation of these objects, call freeAll() or freeUnused() as needed
+     mustSort=sortbyloc;
+     }
+   void sortedByLoc(bool v=true) {
+     bool prev=mustSort;
+     mustSort=v;
+     if (fCount>0 && mustSort && !prev) {
+       this->setSorted((GCompareProc*)gfo_cmpByLoc);
+       }
+     }
    void finalize(GffReader* gfr, bool mergeCloseExons) { //if set, enforce sort by locus
      if (mustSort) { //force (re-)sorting
         this->setSorted(false);
         this->setSorted((GCompareProc*)gfo_cmpByLoc);
         }
+     int delcount=0;
      for (int i=0;i<Count();i++) {
-       fList[i]->finalize(gfr, mergeCloseExons); 
-       //finalize the parsing for this GffObj -- this might also merge close exons/features if requested
+       //finalize the parsing for each GffObj
+       fList[i]->finalize(gfr, mergeCloseExons);
        }
+     if (delcount>0) this->Pack();
      }
+   void freeAll() {
+     for (int i=0;i<fCount;i++) {
+       delete fList[i];
+       fList[i]=NULL;
+       }
+     Clear();
+     }
+   void freeUnused() {
+     for (int i=0;i<fCount;i++) {
+       if (fList[i]->isUsed()) continue;
+       //inform the children
+       for (int c=0;c<fList[i]->children.Count();c++) {
+          fList[i]->children[c]->parent=NULL;
+          }
+       delete fList[i];
+       fList[i]=NULL;
+       }
+     Clear();
+     }
+
 };
 
 class GfoHolder {
@@ -773,43 +937,60 @@ class GffReader {
   off_t fpos;
   int buflen;
  protected:
-  bool gff_warns; //warn about duplicate IDs, even when they are on different chromosomes
+  bool gff_warns; //warn about duplicate IDs, etc. even when they are on different chromosomes
   FILE* fh;
   char* fname;  //optional fasta file with the underlying genomic sequence to be attached to this reader
   GffNames* names; //just a pointer to the global static Gff names repository in GffObj
   GffLine* gffline;
-  bool mrnaOnly; //read only mRNAs ? (exon/CDS features only)
-  //bool sortbyloc; //sort by location: genomic sequence and start coordinate
+  bool transcriptsOnly; //keep only transcripts w/ their exon/CDS features
+  GHash<int> discarded_ids; //for transcriptsOnly mode, keep track
+                            // of discarded parent IDs
   GHash<GfoHolder> phash; //transcript_id+contig (Parent~Contig) => [gflst index, GffObj]
   GHash<int> tids; //transcript_id uniqueness
   char* gfoBuildId(const char* id, const char* ctg);
   void gfoRemove(const char* id, const char* ctg);
   GfoHolder* gfoAdd(const char* id, const char* ctg, GffObj* gfo, int idx);
   GfoHolder* gfoFind(const char* id, const char* ctg);
+  CNonExon* subfPoolCheck(GffLine* gffline, GHash<CNonExon>& pex, char*& subp_name);
+  void subfPoolAdd(GHash<CNonExon>& pex, GfoHolder* newgfo);
+  GfoHolder* promoteFeature(CNonExon* subp, char*& subp_name, GHash<CNonExon>& pex,
+                                  bool keepAttr, bool noExonAttr);
  public:
-  GfList gflst; //accumulate GffObj being read
-  GfoHolder* newGffRec(GffLine* gffline, bool keepAttr, bool noExonAttr, int replaceidx=-1);
-  bool addSubFeature(GfoHolder* prevgfo, GffLine* gffline, GHash<CNonExon>& pex, bool noExonAttr);
+  GfList gflst; //accumulate GffObjs being read
+  GfoHolder* newGffRec(GffLine* gffline, bool keepAttr, bool noExonAttr,
+                               GffObj* parent=NULL, GffExon* pexon=NULL);
+  GfoHolder* replaceGffRec(GffLine* gffline, bool keepAttr, bool noExonAttr, int replaceidx);
+  GfoHolder* updateGffRec(GfoHolder* prevgfo, GffLine* gffline, 
+                                         bool keepAttr, bool noExonAttr);
+  GfoHolder* updateParent(GfoHolder* newgfh, GffObj* parent);
+  bool addExonFeature(GfoHolder* prevgfo, GffLine* gffline, GHash<CNonExon>& pex, bool noExonAttr);
   GList<GSeqStat> gseqstats; //list of all genomic sequences seen by this reader, accumulates stats
-  GffReader(FILE* f, bool justmrna=false, bool sort=false):phash(true), 
-                             tids(true), gflst(sort), gseqstats(true,true,true) {
-      gff_warns=false;
+  GffReader(FILE* f=NULL, bool t_only=false, bool sortbyloc=false):discarded_ids(true),
+                       phash(true), tids(true), gflst(sortbyloc), gseqstats(true,true,true) {
+      gff_warns=gff_show_warnings;
       names=NULL;
       gffline=NULL;
-      mrnaOnly=justmrna;
+      transcriptsOnly=t_only;
       fpos=0;
       fname=NULL;
       fh=f;
       GMALLOC(linebuf, GFF_LINELEN);
       buflen=GFF_LINELEN-1;
       }
-  GffReader(char* fn, bool justmrna=false, bool sort=false):phash(true),
+  void init(FILE *f, bool t_only=false, bool sortbyloc=false) {
+      fname=NULL;
+      fh=f;
+      if (fh!=NULL) rewind(fh);
+      fpos=0;
+      transcriptsOnly=t_only;
+      gflst.sortedByLoc(sortbyloc);
+      }
+  GffReader(char* fn, bool t_only=false, bool sort=false):discarded_ids(true), phash(true),
                              tids(true),gflst(sort),gseqstats(true,true,true) {
-      gff_warns=false;
+      gff_warns=gff_show_warnings;
       names=NULL;
       fname=Gstrdup(fn);
-      mrnaOnly=justmrna;
-      //sortbyloc=sort;
+      transcriptsOnly=t_only;
       fh=fopen(fname, "rb");
       fpos=0;
       gffline=NULL;
@@ -817,11 +998,13 @@ class GffReader {
       buflen=GFF_LINELEN-1;
       }
 
-  virtual ~GffReader() {
+ ~GffReader() {
+      delete gffline;
       gffline=NULL;
       fpos=0;
-      delete gffline;
+      gflst.freeUnused();
       gflst.Clear();
+      discarded_ids.Clear();
       phash.Clear();
       gseqstats.Clear();
       GFREE(fname);
@@ -830,7 +1013,9 @@ class GffReader {
 
   void showWarnings(bool v=true) {
       gff_warns=v;
+      gff_show_warnings=v;
       }
+      
   GffLine* nextGffLine();
 
   // load all subfeatures, re-group them:
diff --git a/src/gffread.cpp b/src/gffread.cpp
new file mode 100644
index 0000000..a490c9b
--- /dev/null
+++ b/src/gffread.cpp
@@ -0,0 +1,944 @@
+#include "gff.h"
+#include "GStr.h"
+#include "GArgs.h"
+#include "GHash.hh"
+#include "GList.hh"
+#include "GFastaIndex.h"
+#include "GFaSeqGet.h"
+#include <ctype.h>
+// don't care about cdb compression
+//#ifdef ENABLE_COMPRESSION
+//#undef ENABLE_COMPRESSION
+//#endif
+//#include "GCdbYank.h"
+
+#define USAGE "Usage:\n\
+gffread <input_gff> [-g <genomic_seqs_fasta> | <dir>][-s <seq_info.fsize>] \n\
+ [-o <outfile.gff>] [-t <tname>] [-r [[<strand>]<chr>:]<start>..<end>] \n\
+ [-CTVNMAFGRUVBHSZWTOE] [-w <spl_exons.fa>] [-x <spl_cds.fa>] [-y <tr_cds.fa>]\n\
+ [-i <maxintron>] \n\
+ Filters and/or converts GFF3/GTF2 records.\n\
+ <input_gff> is a GFF file, use '-' if the GFF records will be given at stdin\n\
+ \n\
+ Options:\n\
+  -g  full path to a multi-fasta file with the genomic sequences\n\
+      for all input mappings, OR a directory with single-fasta files\n\
+      (one per genomic sequence, with file names matching sequence names)\n\
+  -s  <seq_info.fsize> is a tab-delimited file providing this info\n\
+      for each of the mapped sequences:\n\
+      <seq-name> <seq-length> <seq-description>\n\
+      (useful for mRNA/EST/protein mappings with -A option)\n\
+  -i  discard transcripts having an intron larger than <maxintron>\n\
+  -r  only show transcripts crossing coordinate range <start>..<end>\n\
+      (on chromosome/contig <chr>, strand <strand> if provided)\n\
+  -R  for -r option, discard all transcripts that are not fully \n\
+      contained within given range\n\
+  -U  discard single-exon transcripts\n\
+  -C  discard mRNAs that have no CDS feature\n\
+  -F  keep all attributes from last column of GFF/GTF\n\
+  -G  only parse additional exon attributes from the first exon\n\
+      and move them to the mRNA level (useful for GTF input)\n\
+  -A  use the description field from <seq_info.fsize> and add it\n\
+      as the value for a 'descr' attribute to the GFF record\n\
+  \n\
+  -O  process non-transcript GFF records as well (by default non-transcript\
+      records are ignored).\n\
+  -V  discard any mRNAs with CDS having in-frame stop codons\n\
+  -H  for -V option, check and adjust the starting CDS phase\n\
+      if the original phase leads to a translation with an \n\
+      in-frame stop codon\n\
+  -B  for -V option, single-exon transcripts are also checked on the\n\
+      opposite strand\n\
+  -N  only show multi-exon mRNAs if all their introns have the \n\
+      typical splice site consensus ( GT-AG, GC-AG or AT-AC )\n\
+  -M  discard any mRNAs that either lack initial START codon\n\
+      or the terminal STOP codon, or have an in-frame stop codon\n\
+      (only print mRNAs with a fulll, valid CDS)\n\
+ \n\
+  -E  expose (warn about) duplicate transcript IDs and other potential \n\
+      problems with the input GFF/GTF records\n\
+  -S  sort output GFF records by genomic sequence and start coordinate\n\
+      (this option is automatically enabled by -g option)\n\
+  -Z  merge close exons into a single exon (for intron size<4)\n\
+  -w  write a fasta file with spliced exons for each GFF transcript\n\
+  -x  write a fasta file with spliced CDS for each GFF transcript\n\
+  -W  for -w and -x options, also write for each fasta record the exon\n\
+      coordinates projected onto the spliced sequence\n\
+  -y  write a protein fasta file with the translation of CDS for each record\n\
+  -o  the \"filtered\" GFF records will be written to <outfile.gff>\n\
+      (use -o- for printing to stdout)\n\
+  -t  use <trackname> in the second column of each GFF output line\n\
+  -T  -o option will output GTF format instead of GFF3\n\
+ "
+
+FILE* ffasta=NULL;
+FILE* f_in=NULL;
+FILE* f_out=NULL;
+FILE* f_w=NULL; //fasta with spliced exons (transcripts)
+FILE* f_x=NULL; //fasta with spliced CDS
+FILE* f_y=NULL; //fasta with translated CDS
+bool wCDSonly=false;
+
+bool validCDSonly=false; // translation with no in-frame STOP
+bool bothStrands=false; //for single-exon mRNA validation, check the other strand too
+bool altPhases=false; //if original phase fails translation validation,
+                     //try the other 2 phases until one makes it
+bool mRNAOnly=true; 
+bool spliceCheck=false; //only known splice-sites
+
+bool fullCDSonly=false; // starts with START, ends with STOP codon
+bool fullattr=false;
+bool sortByLoc=false; // if the GFF output should be sorted by location
+//GStr gseqpath;
+//GStr gcdbfa;
+//bool multiGSeq=false; //if a directory or a .cidx file was given to -g option
+//GFaSeqGet* faseq=NULL;
+//GCdbYank* gcdb=NULL;
+//int gseq_id=-1; //current genome sequence ID -- the current GffObj::gseq_id
+bool fmtGTF=false;
+bool addDescr=false;
+//bool protmap=false;
+bool multiExon=false;
+bool writeExonSegs=false;
+char* tracklabel=NULL;
+int maxintron=999000000;
+bool mergeCloseExons=false;
+//range filter:
+char* rfltGSeq=NULL;
+char rfltStrand=0;
+uint rfltStart=0;
+uint rfltEnd=MAX_UINT;
+bool rfltWithin=false; //check for full containment within given range
+bool noExonAttr=false;
+class SeqInfo {
+ public:
+  int len;
+  char* descr;
+  SeqInfo( int l, char* s) {
+   len=l;
+   if (s==NULL) {
+     descr=NULL;
+     }   else {
+     descr=Gstrdup(s);
+     }
+   }
+  ~SeqInfo() {
+   GFREE(descr);
+   }
+};
+
+char* getGSeqName(int gseq_id) {
+ return GffObj::names->gseqs.getName(gseq_id);
+}
+
+//genomic fasta sequence handling
+class GFastaHandler {
+ public:
+  char* fastaPath;
+  GFastaIndex* faIdx; //could be a cdb .cidx file
+  int last_fetchid;
+  GFaSeqGet* faseq;
+  //GCdbYank* gcdb;
+  char* getFastaFile(int gseq_id) {
+     if (fastaPath==NULL) return NULL;
+     GStr s(fastaPath);
+     s.trimR('/');
+     s.appendfmt("/%s",getGSeqName(gseq_id));
+     GStr sbase(s);
+     if (!fileExists(s.chars())) s.append(".fa");
+     if (!fileExists(s.chars())) s.append("sta");
+     if (fileExists(s.chars())) return Gstrdup(s.chars());
+         else {
+             GMessage("Warning: cannot find genomic sequence file %s{.fa,.fasta}\n",sbase.chars());
+             return NULL;
+             }
+     }
+
+   GFastaHandler(const char* fpath=NULL) {
+     //gcdb=NULL;
+     fastaPath=NULL;
+     faseq=NULL;
+     faIdx=NULL;
+     init(fpath);
+     }
+
+   void init(const char* fpath) {
+     if (fpath==NULL || fpath[0]==0) return;
+     last_fetchid=-1;
+     if (!fileExists(fpath))
+       GError("Error: file/directory %s does not exist!\n",fpath);
+     fastaPath=Gstrdup(fpath);
+     GStr gseqpath(fpath);
+     /*
+     if (gseqpath.rindex(".cidx")==gseqpath.length()-5) {
+        //cdbyank index given directly
+        gcdb=new GCdbYank(gseqpath.chars());
+        if (fileExists(gcdb->getDbName())) {
+            gseqpath=gcdb->getDbName();
+            } else {
+            gseqpath.chomp(".cidx");
+            if (!fileExists(gseqpath.chars()))
+                GError("Error: cannot locate the fasta file for index %s.cidx !\n",gseqpath.chars());
+            }
+        GFREE(fastaPath);
+        fastaPath=Gstrdup(gseqpath.chars());
+        return;
+        }
+        */
+     if (fileExists(fastaPath)>1) { //exists and it's not a directory
+            GStr fainame(fastaPath);
+            if (fainame.rindex(".fai")==fainame.length()-4) {
+               //.fai index file given directly
+               fastaPath[fainame.length()-4]=0;
+               if (!fileExists(fastaPath))
+                  GError("Error: cannot find fasta file for index %s !\n", fastaPath);
+               }
+              else fainame.append(".fai");
+            //GMessage("creating GFastaIndex with fastaPath=%s, fainame=%s\n", fastaPath, fainame.chars());
+            faIdx=new GFastaIndex(fastaPath,fainame.chars());
+            GStr fainamecwd(fainame);
+            int ip=-1;
+            if ((ip=fainamecwd.rindex(CHPATHSEP))>=0)
+               fainamecwd.cut(0,ip+1);
+            if (!faIdx->hasIndex()) { //could not load index
+               //try current directory
+                  if (fainame!=fainamecwd) {
+                    if (fileExists(fainamecwd.chars())>1) {
+                       faIdx->loadIndex(fainamecwd.chars());
+                       }
+                    }
+                  } //tried to load index
+            if (!faIdx->hasIndex()) {
+                 GMessage("No fasta index found for %s. Rebuilding, please wait..\n",fastaPath);
+                 faIdx->buildIndex();
+                 if (faIdx->getCount()==0) GError("Error: no fasta records found!\n");
+                 GMessage("Fasta index rebuilt.\n");
+                 FILE* fcreate=fopen(fainame.chars(), "w");
+                 if (fcreate==NULL) {
+                   GMessage("Warning: cannot create fasta index %s! (permissions?)\n", fainame.chars());
+                   if (fainame!=fainamecwd) fcreate=fopen(fainamecwd.chars(), "w");
+                   if (fcreate==NULL)
+                      GError("Error: cannot create fasta index %s!\n", fainamecwd.chars());
+                   }
+                 if (faIdx->storeIndex(fcreate)<faIdx->getCount())
+                     GMessage("Warning: error writing the index file!\n");
+                 } //index created and attempted to store it
+            } //multi-fasta
+     }
+   GFaSeqGet* fetch(int gseq_id, bool checkFasta=false) {
+     if (fastaPath==NULL) return NULL;
+     if (gseq_id==last_fetchid && faseq!=NULL) return faseq;
+     delete faseq;
+     faseq=NULL;
+     last_fetchid=-1;
+     char* gseqname=getGSeqName(gseq_id);
+     // DEBUG:
+     //GMessage("..processing transcripts on: %s\n",gseqname);
+     //genomic sequence given
+     /*
+     if (gcdb!=NULL) {
+       uint32 reclen=0;
+       off_t rpos=gcdb->getRecordPos(gseqname, &reclen);
+       if (rpos<0) // genomic sequence not found
+          GError("Error: cannot find genomic sequence '%s' in %s\n",gseqname, fastaPath);
+       // WARNING: does not validate FASTA line-len uniformity!
+       faseq=new GFaSeqGet(fastaPath,rpos, false);
+       faseq->loadall(reclen); //load the whole sequence, it's faster
+       last_fetchid=gseq_id;
+       return faseq;
+       }
+       */
+     if (faIdx!=NULL) { //fastaPath was the multi-fasta file name
+        GFastaRec* farec=faIdx->getRecord(gseqname);
+        if (farec!=NULL) {
+             faseq=new GFaSeqGet(fastaPath,farec->seqlen, farec->fpos,
+                               farec->line_len, farec->line_blen);
+             faseq->loadall(); //just cache the whole sequence, it's faster
+             last_fetchid=gseq_id;
+             }
+        else {
+          GMessage("Warning: couldn't find fasta record for '%s'!\n",gseqname);
+          return NULL;
+          }
+        }
+     else {
+         char* sfile=getFastaFile(gseq_id);
+         if (sfile!=NULL) {
+            faseq=new GFaSeqGet(sfile,checkFasta);
+            faseq->loadall();
+            last_fetchid=gseq_id;
+            GFREE(sfile);
+            }
+         } //one fasta file per contig
+       return faseq;
+     }
+
+   ~GFastaHandler() {
+     GFREE(fastaPath);
+     //delete gcdb;
+     delete faIdx;
+     delete faseq;
+     }
+};
+
+
+class GSpliceSite {
+ public:
+  char nt[3];
+  GSpliceSite(const char* c, bool revc=false) {
+    nt[2]=0;
+    if (c==NULL) {
+      nt[0]=0;
+      nt[1]=0;
+      return;
+      }
+    if (revc) {
+      nt[0]=toupper(ntComplement(c[1]));
+      nt[1]=toupper(ntComplement(c[0]));
+      }
+    else {
+      nt[0]=toupper(c[0]);
+      nt[1]=toupper(c[1]);
+      }
+    }
+
+  GSpliceSite(const char* intron, int intronlen, bool getAcceptor, bool revc=false) {
+    nt[2]=0;
+    if (intron==NULL || intronlen==0)
+       GError("Error: invalid intron or intron len for GSpliceSite()!\n");
+    const char* c=intron;
+    if (revc) {
+      if (!getAcceptor) c+=intronlen-2;
+      nt[0]=toupper(ntComplement(c[1]));
+      nt[1]=toupper(ntComplement(c[0]));
+      }
+    else { //on forward strand
+      if (getAcceptor) c+=intronlen-2;
+      nt[0]=toupper(c[0]);
+      nt[1]=toupper(c[1]);
+      }//forward strand
+    }
+
+  GSpliceSite(const char n1, const char n2) {
+    nt[2]=0;
+    nt[0]=toupper(n1);
+    nt[1]=toupper(n2);
+    }
+  bool canonicalDonor() {
+    return (nt[0]=='G' && (nt[1]=='C' || nt[1]=='T'));
+    }
+  bool operator==(GSpliceSite& c) {
+    return (c.nt[0]==nt[0] && c.nt[1]==nt[1]);
+    }
+  bool operator==(GSpliceSite* c) {
+    return (c->nt[0]==nt[0] && c->nt[1]==nt[1]);
+    }
+  bool operator==(const char* c) {
+    //return (nt[0]==toupper(c[0]) && nt[1]==toupper(c[1]));
+    //assumes given const nucleotides are uppercase already!
+    return (nt[0]==c[0] && nt[1]==c[1]);
+    }
+  bool operator!=(const char* c) {
+    //assumes given const nucleotides are uppercase already!
+    return (nt[0]!=c[0] || nt[1]!=c[1]);
+    }
+};
+
+//hash with sequence info
+GHash<SeqInfo> seqinfo;
+GHash<int> isoCounter; //counts the valid isoforms
+
+//bool debugMode=false;
+bool verbose=false;
+
+char* getSeqDescr(char* seqid) {
+ static char charbuf[128];
+ if (seqinfo.Count()==0) return NULL;
+ char* suf=rstrchr(seqid, '.');
+ if (suf!=NULL) *suf=0;
+ SeqInfo* seqd=seqinfo.Find(seqid);
+ if (suf!=NULL) *suf='.';
+ if (seqd!=NULL) {
+  GStr s(seqd->descr);
+  //cleanup some Uniref gunk
+  if (s[0]=='[') {
+    int r=s.index(']');
+    if (r>=0 && r<8 && isdigit(s[1]))
+       s.remove(0,r+1);
+    }
+  if (s.length()>80) {
+    int r=s.index(';');
+    if (r>5) s.cut(r);
+    }
+  if (s.length()>127) {
+   s.cut(127);
+   int r=s.rindex(' ');
+   if (r>0) s.cut(r);
+   }
+  strcpy(charbuf, s.chars());
+  return charbuf;
+  }
+ else return NULL;
+}
+
+char* getSeqName(char* seqid) {
+  static char charbuf[128];
+  char* suf=rstrchr(seqid, '.');
+  if (suf!=NULL) *suf=0;
+  strcpy(charbuf, seqid);
+  if (suf!=NULL) *suf='.';
+  return charbuf;
+}
+
+void printFasta(FILE* f, GStr& defline, char* seq, int seqlen=-1) {
+ if (seq==NULL) return;
+ int len=(seqlen>0)?seqlen:strlen(seq);
+ if (len<=0) return;
+ if (!defline.is_empty())
+     fprintf(f, ">%s\n",defline.chars());
+ int ilen=0;
+ for (int i=0; i < len; i++, ilen++) {
+   if (ilen == 70) {
+     fputc('\n', f);
+     ilen = 0;
+     }
+   putc(seq[i], f);
+   } //for
+ fputc('\n', f);
+}
+
+void loadSeqInfo(FILE* f, GHash<SeqInfo> &si) {
+  GLineReader fr(f);
+  while (!fr.isEof()) {
+      char* line=fr.getLine();
+      if (line==NULL) break;
+      char* id=line;
+      char* lenstr=NULL;
+      char* text=NULL;
+      char* p=line;
+      while (*p!=0 && !isspace(*p)) p++;
+      if (*p==0) continue;
+      *p=0;p++;
+      while (*p==' ' || *p=='\t') p++;
+      if (*p==0) continue;
+      lenstr=p;
+      while (*p!=0 && !isspace(*p)) p++;
+      if (*p!=0) { *p=0;p++; }
+      while (*p==' ' || *p=='\t') p++;
+      if (*p!=0) text=p; //else text remains NULL
+      int len=0;
+      if (!parseInt(lenstr,len)) {
+         GMessage("Warning: could not parse sequence length: %s %s\n",
+                  id, lenstr);
+         continue;
+         }
+      // --- here we have finished parsing the line
+      si.Add(id, new SeqInfo(len,text));
+      } //while lines
+}
+
+GFaSeqGet* fastaSeqGet(GFastaHandler& gfasta, GffObj& mrna) {
+  if (gfasta.fastaPath==NULL) return NULL;
+  return gfasta.fetch(mrna.gseq_id);
+}
+
+int adjust_stopcodon(GffObj& mrna, int adj, GList<GSeg>* seglst=NULL) {
+ //adj>0 => extedn CDS,  adj<0 => shrink CDS
+ //when CDS is expanded, exons have to be checked too and 
+ // expanded accordingly if they had the same boundary
+  int realadj=0;
+  if (mrna.strand=='-') {
+       if ((int)mrna.CDstart>adj) {
+
+           mrna.CDstart-=adj;
+           realadj=adj;
+           if (adj<0) { //restore
+              if (mrna.exons.First()->start==mrna.CDstart+adj) {
+                 mrna.exons.First()->start-=adj;
+                 mrna.start=mrna.exons.First()->start;
+                 mrna.covlen+=adj;
+                 }
+              }
+           else if (mrna.exons.First()->start>=mrna.CDstart) {
+                 mrna.exons.First()->start-=adj;
+                 mrna.start=mrna.exons.First()->start;
+                 mrna.covlen+=adj;
+                 }
+             }
+          }
+        else {
+         realadj=adj;
+         mrna.CDend+=adj;
+         if (adj<0) {//restore
+           if (mrna.exons.Last()->end==mrna.CDend-adj) {
+                        mrna.exons.Last()->end+=adj;
+                        mrna.end=mrna.exons.Last()->end;
+                        mrna.covlen+=adj;
+                        }
+          }
+         else if (mrna.exons.Last()->end<=mrna.CDend) {
+             mrna.exons.Last()->end+=adj;
+             mrna.end=mrna.exons.Last()->end;
+             mrna.covlen+=adj;
+             }
+         }
+  if (seglst!=NULL) seglst->Last()->end+=adj;
+  return realadj;
+ }
+
+void process_mRNA(GFastaHandler& gfasta, GffObj& mrna) {
+  if (f_out!=NULL && !mRNAOnly && mrna.exons.Count()==0) {
+     //a gene or other generic feature without exons
+     if (fmtGTF) mrna.printGtf(f_out, tracklabel);
+            else mrna.printGff(f_out, tracklabel);
+     return;
+     }
+ char* gname=mrna.getGeneName();
+ if (gname==NULL) gname=mrna.getGeneID();
+ GStr defline(mrna.getID());
+ if (gname && strcmp(gname,mrna.getID())!=0) {
+   int* isonum=isoCounter.Find(gname);
+   if  (isonum==NULL) {
+       isonum=new int(1);
+       isoCounter.Add(gname,isonum);
+       }
+      else (*isonum)++;
+   defline.appendfmt(" gene=%s", gname);
+   }
+  int seqlen=0;
+
+  const char* tlabel=tracklabel;
+  if (tlabel==NULL) tlabel=mrna.getTrackName();
+  //defline.appendfmt(" track:%s",tlabel);
+  char* cdsnt = NULL;
+  char* cdsaa = NULL;
+  int aalen=0;
+  for (int i=1;i<mrna.exons.Count();i++) {
+     int ilen=mrna.exons[i]->start-mrna.exons[i-1]->end-1;
+     if (ilen>2000000) 
+            GMessage("Warning: very large intron (%d) for transcript %s\n",
+                           ilen, mrna.getID());
+     if (ilen>maxintron) {
+         return;
+         }
+     }
+  GList<GSeg> seglst(false,true);
+  GFaSeqGet* faseq=fastaSeqGet(gfasta, mrna);
+  if (spliceCheck && mrna.exons.Count()>1) {
+    //check introns for splice site consensi ( GT-AG, GC-AG or AT-AC )
+    if (faseq==NULL) GError("Error: no genomic sequence available!\n");
+    int glen=mrna.end-mrna.start+1;
+    const char* gseq=faseq->subseq(mrna.start, glen);
+    bool revcompl=(mrna.strand=='-');
+    bool ssValid=true;
+    for (int e=1;e<mrna.exons.Count();e++) {
+      const char* intron=gseq+mrna.exons[e-1]->end+1-mrna.start;
+      int intronlen=mrna.exons[e]->start-mrna.exons[e-1]->end-1;
+      GSpliceSite acceptorSite(intron,intronlen,true, revcompl);
+      GSpliceSite    donorSite(intron,intronlen, false, revcompl);
+      //GMessage("%c intron %d-%d : %s .. %s\n",
+      //           mrna.strand, istart, iend, donorSite.nt, acceptorSite.nt);
+      if (acceptorSite=="AG") { // GT-AG or GC-AG
+         if (!donorSite.canonicalDonor()) {
+            ssValid=false;break;
+            }
+         }
+      else if (acceptorSite=="AC") { //
+         if (donorSite!="AT") { ssValid=false; break; }
+         }
+      else { ssValid=false; break; }
+      }
+    //GFREE(gseq);
+    if (!ssValid) {
+      if (verbose)
+         GMessage("Invalid splice sites found for '%s'\n",mrna.getID());
+      return; //don't print this one!
+      }
+    }
+
+  bool trprint=true;
+  int stopCodonAdjust=0;
+  int mCDphase=0;
+  bool hasStop=false;
+  if (mrna.CDphase=='1' || mrna.CDphase=='2')
+      mCDphase = mrna.CDphase-'0';
+  if (f_y!=NULL || f_x!=NULL || validCDSonly) {
+    if (faseq==NULL) GError("Error: no genomic sequence provided!\n");
+    //if (protmap && fullCDSonly) {
+    //if (protmap && (fullCDSonly ||  (mrna.qlen>0 && mrna.qend==mrna.qlen))) {
+    
+    if (validCDSonly) { //make sure the stop codon is always included 
+      //adjust_stopcodon(mrna,3);
+      stopCodonAdjust=adjust_stopcodon(mrna,3);
+      }
+    int strandNum=0;
+    int phaseNum=0;
+  CDS_CHECK:
+    cdsnt=mrna.getSpliced(faseq, true, &seqlen,NULL,NULL,&seglst);
+    if (cdsnt==NULL) trprint=false;
+    if (validCDSonly) {
+       cdsaa=translateDNA(cdsnt, aalen, seqlen);
+       char* p=strchr(cdsaa,'.');
+       hasStop=false;
+       if (p!=NULL) {
+            if (p-cdsaa>=aalen-2) { //stop found as the last codon
+                    *p='0';//remove it
+                    hasStop=true;
+                    if (aalen-2==p-cdsaa) {
+                      //previous to last codon is the stop codon
+                      //so correct the CDS stop accordingly
+                      adjust_stopcodon(mrna,-3, &seglst);
+                      stopCodonAdjust=0; //clear artificial stop adjustment
+                      seqlen-=3;
+                      cdsnt[seqlen]=0;
+                      }
+                    aalen=p-cdsaa;
+                    }
+                 else {//stop found before the last codon
+                    trprint=false;
+                    }
+            }//stop codon found
+       if (trprint==false) { //failed CDS validity check
+         //in-frame stop codon found
+         if (altPhases && phaseNum<3) {
+            phaseNum++;
+            mrna.CDphase = '0'+((mCDphase+phaseNum)%3);
+            GFREE(cdsaa);
+            goto CDS_CHECK;
+            }
+         if (mrna.exons.Count()==1 && bothStrands) {
+            strandNum++;
+            phaseNum=0;
+            if (strandNum<2) {
+               GFREE(cdsaa);
+               mrna.strand = (mrna.strand=='-') ? '+':'-';
+               goto CDS_CHECK; //repeat the CDS check for a different frame
+               }
+            }
+         if (verbose) GMessage("In-frame STOP found for '%s'\n",mrna.getID());
+         } //has in-frame STOP
+       if (fullCDSonly) {
+           if (!hasStop || cdsaa[0]!='M') trprint=false;
+           }
+       } // CDS check requested
+    } //translation or codon check/output was requested
+  if (!trprint) {
+    GFREE(cdsnt);
+    GFREE(cdsaa);
+    return;
+    }
+  if (stopCodonAdjust>0 && !hasStop) {
+          //restore stop codon location
+          adjust_stopcodon(mrna, -stopCodonAdjust, &seglst);
+          if (cdsnt!=NULL && seqlen>0) {
+             seqlen-=stopCodonAdjust;
+             cdsnt[seqlen]=0;
+             }
+          if (cdsaa!=NULL) aalen--;
+          }
+
+  if (f_out!=NULL) {
+     if (fmtGTF) mrna.printGtf(f_out, tracklabel);
+            else mrna.printGff(f_out, tracklabel);
+     }
+  if (f_y!=NULL) { //CDS translation fasta output requested
+         //char* 
+         if (cdsaa==NULL) { //translate now if not done before
+           cdsaa=translateDNA(cdsnt, aalen, seqlen);
+           }
+         if (fullattr && mrna.attrs!=NULL) {
+             //append all attributes found for each transcripts
+              for (int i=0;i<mrna.attrs->Count();i++) {
+                defline.append(" ");
+                defline.append(mrna.getAttrName(i));
+                defline.append("=");
+                defline.append(mrna.getAttrValue(i));
+                }
+              }
+         printFasta(f_y, defline, cdsaa, aalen);
+         }
+   if (f_x!=NULL) { //CDS only
+         if (writeExonSegs) {
+              defline.append(" loc:");
+              defline.append(mrna.getGSeqName());
+              defline.appendfmt("(%c)",mrna.strand);
+              //warning: not CDS coordinates are written here, but the exon ones
+              defline+=(int)mrna.start;
+              defline+=(char)'-';
+              defline+=(int)mrna.end;
+              // -- here these are CDS substring coordinates on the spliced sequence:
+              defline.append(" segs:");
+              for (int i=0;i<seglst.Count();i++) {
+                  if (i>0) defline.append(",");
+                  defline+=(int)seglst[i]->start;
+                  defline.append("-");
+                  defline+=(int)seglst[i]->end;
+                  }
+              }
+         if (fullattr && mrna.attrs!=NULL) {
+             //append all attributes found for each transcript
+              for (int i=0;i<mrna.attrs->Count();i++) {
+                defline.append(" ");
+                defline.append(mrna.getAttrName(i));
+                defline.append("=");
+                defline.append(mrna.getAttrValue(i));
+                }
+              }
+         printFasta(f_x, defline, cdsnt, seqlen);
+         }
+ GFREE(cdsnt);
+ GFREE(cdsaa);
+ if (f_w!=NULL) { //write spliced exons
+    uint cds_start=0;
+    uint cds_end=0;
+    seglst.Clear();
+    char* exont=mrna.getSpliced(faseq, false, &seqlen, &cds_start, &cds_end, &seglst);
+    if (exont!=NULL) {
+    if (mrna.CDstart>0) {
+        defline.appendfmt(" CDS=%d-%d", cds_start, cds_end);
+        }
+      if (writeExonSegs) {
+        defline.append(" loc:");
+        defline.append(mrna.getGSeqName());
+        defline+=(char)'|';
+        defline+=(int)mrna.start;
+        defline+=(char)'-';
+        defline+=(int)mrna.end;
+        defline+=(char)'|';
+        defline+=(char)mrna.strand;
+        defline.append(" exons:");
+        for (int i=0;i<mrna.exons.Count();i++) {
+                if (i>0) defline.append(",");
+                defline+=(int)mrna.exons[i]->start;
+                defline.append("-");
+                defline+=(int)mrna.exons[i]->end;
+                }
+        defline.append(" segs:");
+        for (int i=0;i<seglst.Count();i++) {
+            if (i>0) defline.append(",");
+            defline+=(int)seglst[i]->start;
+            defline.append("-");
+            defline+=(int)seglst[i]->end;
+            }
+        }
+      if (fullattr && mrna.attrs!=NULL) {
+       //append all attributes found for each transcripts
+        for (int i=0;i<mrna.attrs->Count();i++) {
+          defline.append(" ");
+          defline.append(mrna.getAttrName(i));
+          defline.append("=");
+          defline.append(mrna.getAttrValue(i));
+          }
+        }
+      printFasta(f_w, defline, exont, seqlen);
+      GFREE(exont);
+      }
+    } //writing f_w (spliced exons)
+ return;
+}
+
+void openfw(FILE* &f, GArgs& args, char opt) {
+  GStr s=args.getOpt(opt);
+  if (!s.is_empty()) {
+      if (s=='-')
+       f=stdout;
+      else {
+       f=fopen(s,"w");
+       if (f==NULL) GError("Error creating file: %s\n", s.chars());
+       }
+     }
+}
+
+#define FWCLOSE(fh) if (fh!=NULL && fh!=stdout) fclose(fh)
+#define FRCLOSE(fh) if (fh!=NULL && fh!=stdin) fclose(fh)
+
+
+int main(int argc, char * const argv[]) {
+ GArgs args(argc, argv, "hvOUNHWCVMNSXTDAPRZFGEg:i:r:s:t:a:b:o:w:x:y:MINCOV=MINPID=");
+ int e;
+ if ((e=args.isError())>0)
+    GError("%s\nInvalid argument: %s\n", USAGE, argv[e]);
+ if (args.getOpt('h')!=NULL) GError("%s\n", USAGE);
+ //debugMode=(args.getOpt('D')!=NULL);
+ mRNAOnly=(args.getOpt('O')==NULL);
+ sortByLoc=(args.getOpt('S')!=NULL);
+ addDescr=(args.getOpt('A')!=NULL);
+ verbose=(args.getOpt('v')!=NULL);
+ wCDSonly=(args.getOpt('C')!=NULL);
+ validCDSonly=(args.getOpt('V')!=NULL);
+ altPhases=(args.getOpt('H')!=NULL);
+ fmtGTF=(args.getOpt('T')!=NULL); //switch output format to GTF
+ bothStrands=(args.getOpt('B')!=NULL);
+ fullCDSonly=(args.getOpt('M')!=NULL);
+ spliceCheck=(args.getOpt('N')!=NULL);
+ //protmap=(args.getOpt('P')!=NULL);
+ if (fullCDSonly) validCDSonly=true;
+ fullattr=(args.getOpt('F')!=NULL);
+ if (args.getOpt('G')==NULL) 
+    noExonAttr=!fullattr;
+   else {
+     noExonAttr=true;
+     fullattr=true;
+     }
+ mergeCloseExons=(args.getOpt('Z')!=NULL);
+ multiExon=(args.getOpt('U')!=NULL);
+ writeExonSegs=(args.getOpt('W')!=NULL);
+ tracklabel=args.getOpt('t');
+ GFastaHandler gfasta(args.getOpt('g'));
+ if (gfasta.fastaPath!=NULL) sortByLoc=true; //enforce sorting by chromosome/contig
+ GStr s=args.getOpt('i');
+ if (!s.is_empty()) maxintron=s.asInt();
+ rfltWithin=(args.getOpt('R')!=NULL);
+ s=args.getOpt('r');
+ if (!s.is_empty()) {
+   s.trim();
+   if (s[0]=='+' || s[0]=='-') {
+     rfltStrand=s[0];
+     s.cut(0,1);
+     }
+   int isep=s.index(':');
+   if (isep>0) { //gseq name given
+      if (rfltStrand==0 && (s[isep-1]=='+' || s[isep-1]=='-')) {
+        isep--;
+        rfltStrand=s[isep];
+        s.cut(isep,1);
+        }
+      if (isep>0) 
+          rfltGSeq=Gstrdup((s.substr(0,isep)).chars());
+      s.cut(0,isep+1);
+      }
+   GStr gsend;
+   char slast=s[s.length()-1];
+   if (rfltStrand==0 && (slast=='+' || slast=='-')) {
+      s.chomp(slast);
+      rfltStrand=slast;
+      }
+   if (s.index("..")>=0) gsend=s.split("..");
+                    else gsend=s.split('-');
+   if (!s.is_empty()) rfltStart=(uint)s.asInt();
+   if (!gsend.is_empty()) {
+      rfltEnd=(uint)gsend.asInt();
+      if (rfltEnd==0) rfltEnd=MAX_UINT;
+      }
+   
+   } //gseq/range filtering
+ else {
+   if (rfltWithin)
+     GError("Error: option -R doesn't make sense without -r!\n");
+   }
+ s=args.getOpt('s');
+ if (!s.is_empty()) {
+  FILE* fsize=fopen(s,"r");
+  if (fsize==NULL) GError("Error opening info file: %s\n",s.chars());
+  loadSeqInfo(fsize, seqinfo);
+ }
+ /*
+ openfw(fgtfok, args, 'a');
+ openfw(fgtfbad, args, 'b');
+ */
+ openfw(f_out, args, 'o');
+ //if (f_out==NULL) f_out=stdout;
+ if (gfasta.fastaPath==NULL && (validCDSonly || spliceCheck || args.getOpt('w')!=NULL || args.getOpt('x')!=NULL || args.getOpt('y')!=NULL))
+  GError("Error: -g option is required for options -w, -x, -y, -V, -N, -M !\n");
+
+ openfw(f_w, args, 'w');
+ openfw(f_x, args, 'x');
+ openfw(f_y, args, 'y');
+ if (f_y!=NULL || f_x!=NULL) wCDSonly=true;
+ //useBadCDS=useBadCDS || (fgtfok==NULL && fgtfbad==NULL && f_y==NULL && f_x==NULL);
+ GStr infile;
+ if (args.startNonOpt()) {
+        infile=args.nextNonOpt();
+        //GMessage("Given file: %s\n",infile.chars());
+        }
+ if (!infile.is_empty()) {
+    if (infile=="-") f_in=stdin;
+      else 
+        if ((f_in=fopen(infile, "r"))==NULL)
+            GError("Cannot open input file %s!\n",infile.chars());
+    }
+  else
+    f_in=stdin;
+
+ //GffReader* gfreader=new GffReader(f_in,true);
+ GffReader* gfreader=new GffReader(f_in, mRNAOnly, sortByLoc);
+ if (args.getOpt('E')!=NULL) gfreader->showWarnings(true);
+ gfreader->readAll(fullattr, mergeCloseExons, noExonAttr);
+
+ //if (debugMode) GMessage("[D] gff data loaded, now processing each entry\n");
+ //GList<GffObj> mrnas(true,true,false);
+ for (int m=0;m<gfreader->gflst.Count();m++) {
+   GffObj* mrna=gfreader->gflst[m];
+   if (mrna->hasErrors() || (mrna->len()+500>GFF_MAX_LOCUS)) { //should probably report these in a file too..
+			GMessage("Warning: transcript %s discarded (structural errors found, length=%d).\n", 
+			                                        mrna->getID(), mrna->len());
+			//gfreader->gflst.freeItem(m);
+			continue;
+			}
+	//GStr feature(mrna->getFeatureName());
+	//feature.lower();
+	//bool gene_or_locus=(feature.endsWith("gene") ||feature.index("loc")>=0);
+	
+	//if (mRNAOnly && mrna->monoFeature() && !mrna->isTranscript() &&
+	//     (mrna->exons.Count()==0 || gene_or_locus)) {
+    if (mRNAOnly && mrna->isDiscarded()) {
+	   //discard generic "gene" or "locus" features with no other detailed subfeatures
+	   //GMessage("Warning: discarding %s GFF generic gene/locus container %s\n",m->getID());
+	   continue;
+	   }
+   //if (mrna->exons.Count()==0 && (mrna->isTranscript() ||
+    //    (!mRNAOnly && !gene_or_locus))) {
+    if (mrna->exons.Count()==0) {
+      //a non-mRNA feature with no subfeatures
+      //just so we get some sequence functions working, add a dummy "exon"-like subfeature here
+      mrna->addExon(mrna->start,mrna->end);
+      }
+   if (rfltGSeq!=NULL) { //filter by gseqName
+      if (strcmp(mrna->getGSeqName(),rfltGSeq)!=0) {
+        continue;
+        }
+      }
+   if (rfltStrand>0 && mrna->strand !=rfltStrand) {
+      continue;
+      }
+   //check coordinates
+   if (rfltStart!=0 || rfltEnd!=MAX_UINT) {
+     if (rfltWithin) {
+       if (mrna->start<rfltStart || mrna->end>rfltEnd) {
+          continue;
+          }
+       }
+     else {
+       if (mrna->start>rfltEnd || mrna->end<rfltStart) {
+         continue;
+         }
+       }
+     }
+   if (multiExon && mrna->exons.Count()<=1) {
+       continue;
+       }
+   if (wCDSonly && mrna->CDstart==0) {
+       continue;
+       }
+   /* if (validCDSonly && mrna->hasErrors) {
+       delete mrna;
+       gfreader->gflst.Forget(m);
+       continue;
+       }
+   */
+   process_mRNA(gfasta, *mrna);
+   }
+ // M_END:
+ delete gfreader;
+ seqinfo.Clear();
+ //if (faseq!=NULL) delete faseq;
+ //if (gcdb!=NULL) delete gcdb;
+ GFREE(rfltGSeq);
+ FRCLOSE(f_in);
+ FWCLOSE(f_out);
+ FWCLOSE(f_w);
+ FWCLOSE(f_x);
+ FWCLOSE(f_y);
+ }
+
+
diff --git a/src/graph_optimize.cpp b/src/graph_optimize.cpp
index 9bce8ab..e877774 100644
--- a/src/graph_optimize.cpp
+++ b/src/graph_optimize.cpp
@@ -105,51 +105,34 @@ void collect_non_redundant_ops(const vector<Scaffold>& scaffolds,
 	sort (ops.begin(), ops.end(), op_left_lt_right_lt);   
 }
 
-void fill_unambiguous_unknowns(vector<Scaffold>& scaffolds)
+void fill_unambiguous_unknowns(vector<Scaffold>& to_fill, 
+                               const vector<Scaffold>& constitutive)
 {
-    vector<AugmentedCuffOp> conflict_ops;
-    vector<AugmentedCuffOp> ops;
-    
-    collect_non_redundant_ops(scaffolds, ops);
-    
-    extract_conflicting_ops(ops, conflict_ops);
-    
-    sort(conflict_ops.begin(), conflict_ops.end());
-    sort(ops.begin(), ops.end());
-    
-    vector<AugmentedCuffOp> non_conflict;
-    
-    set_difference(ops.begin(), 
-                   ops.end(), 
-                   conflict_ops.begin(), 
-                   conflict_ops.end(), 
-                   back_inserter(non_conflict));
-    sort(non_conflict.begin(), non_conflict.end(), AugmentedCuffOp::g_left_lt);
-    vector<AugmentedCuffOp> merged;
-    
-    CuffStrand s = CUFF_STRAND_UNKNOWN;
-    for (size_t i = 0; i < scaffolds.size(); ++i)
+//    vector<AugmentedCuffOp> conflict_ops;
+//    vector<AugmentedCuffOp> ops;
+
+    for (size_t i = 0; i < to_fill.size(); ++i)
     {
-        if (scaffolds[i].strand() != CUFF_STRAND_UNKNOWN)
+        if (to_fill[i].has_unknown())
         {
-            if (s != CUFF_STRAND_UNKNOWN)
-            {
-                assert (s == scaffolds[i].strand());
-            }
-            else 
+            for( size_t j = 0; j < constitutive.size(); ++j)
             {
-                s = scaffolds[i].strand();
+                const Scaffold& cons = constitutive[j];
+                if (Scaffold::overlap_in_genome(to_fill[i], cons, 0) &&
+                    Scaffold::compatible(to_fill[i], cons))
+                {
+                    if (cons.strand() != CUFF_STRAND_UNKNOWN)
+                        to_fill[i].strand(cons.strand());
+
+                    to_fill[i].fill_gaps(cons.augmented_ops());
+                    if (!to_fill[i].has_unknown())
+                    {
+                        break;
+                    }
+                }
             }
-
         }
     }
-    
-    AugmentedCuffOp::merge_ops(non_conflict, merged, true);
-    for (size_t i = 0; i < scaffolds.size(); ++i)
-    {
-        scaffolds[i].strand(s);
-        scaffolds[i].fill_gaps(merged);
-    }
 }
 
 // WARNING: scaffolds MUST be sorted by scaff_lt_rt() in order for this routine
@@ -198,7 +181,7 @@ void add_non_constitutive_to_scaffold_mask(const vector<Scaffold>& scaffolds,
 		}
 	}
     
-	asm_verbose("%lu constitutive reads of %lu smash-filtered from further consideration\n", num_filtered, smash_filter.size());
+	verbose_msg("%lu constitutive reads of %lu smash-filtered from further consideration\n", num_filtered, smash_filter.size());
     
     vector<AugmentedCuffOp> ops;
     collect_non_redundant_ops(scaffolds, ops);
@@ -239,7 +222,7 @@ bool collapse_contained_transfrags(vector<Scaffold>& scaffolds,
 	while (max_rounds--)
 	{
 		
-		asm_verbose("%s\tStarting new collapse round\n", bundle_label->c_str());
+		verbose_msg("%s\tStarting new collapse round\n", bundle_label->c_str());
         
 		ContainmentGraph containment;
 		
@@ -290,12 +273,12 @@ bool collapse_contained_transfrags(vector<Scaffold>& scaffolds,
 		
 		lemon::MaxBipartiteMatching<ContainmentGraph>  matcher(containment);
         
-		asm_verbose("%s\tContainment graph has %d nodes, %d edges\n", bundle_label->c_str(), containment.aNodeNum(), containment.uEdgeNum());
-		asm_verbose("%s\tFinding a maximum matching to collapse scaffolds\n", bundle_label->c_str());
+		verbose_msg("%s\tContainment graph has %d nodes, %d edges\n", bundle_label->c_str(), containment.aNodeNum(), containment.uEdgeNum());
+		verbose_msg("%s\tFinding a maximum matching to collapse scaffolds\n", bundle_label->c_str());
         
 		matcher.run();
         
-		asm_verbose( "%s\tWill collapse %d scaffolds\n", bundle_label->c_str(), matcher.matchingSize());
+		verbose_msg( "%s\tWill collapse %d scaffolds\n", bundle_label->c_str(), matcher.matchingSize());
 		
 		ContainmentGraph::UEdgeMap<bool> matched_edges(containment);
 		
@@ -389,12 +372,12 @@ bool collapse_equivalent_transfrags(vector<Scaffold>& fragments,
 			 smaller_idx_array.end(), 
 			 FragIndexSortSmallerLR(fragments));
 		
-		asm_verbose("%s\tStarting new collapse round\n", bundle_label->c_str());
-        asm_verbose("%s\tFinding fragment-level conflicts\n", bundle_label->c_str());
+		verbose_msg("%s\tStarting new collapse round\n", bundle_label->c_str());
+        verbose_msg("%s\tFinding fragment-level conflicts\n", bundle_label->c_str());
 
         bool will_perform_collapse = false;
 		
-        asm_verbose( "%s\tAssessing overlaps between %lu fragments for identical conflict sets\n", 
+        verbose_msg( "%s\tAssessing overlaps between %lu fragments for identical conflict sets\n", 
                 bundle_label->c_str(), 
                 fragments.size());
         vector<size_t> replacements;
@@ -452,21 +435,24 @@ bool collapse_equivalent_transfrags(vector<Scaffold>& fragments,
 				
 				double lhs_len = lhs_scaff.right() - lhs_scaff.left();
 				
-				for (int i = 0; i < smaller_idx_array.size(); ++i)
+				for (size_t i = 0; i < smaller_idx_array.size(); ++i)
 				{
 					size_t j_scaff_idx = smaller_idx_array[i];
-					if (Scaffold::overlap_in_genome(lhs_scaff, fragments[j_scaff_idx], 0))
-					{
-						if (!Scaffold::compatible(lhs_scaff, fragments[j_scaff_idx]))
-						{
-							curr_conflicts.push_back(j_scaff_idx);
-						}
-					}
+                    if (replacements[j_scaff_idx] == j_scaff_idx)
+                    {
+                        if (Scaffold::overlap_in_genome(lhs_scaff, fragments[j_scaff_idx], 0))
+                        {
+                            if (!Scaffold::compatible(lhs_scaff, fragments[j_scaff_idx]))
+                            {
+                                curr_conflicts.push_back(j_scaff_idx);
+                            }
+                        }
+                    }
 				}
 				sort(curr_conflicts.begin(), curr_conflicts.end());
 				
 				//bool advanced_curr = false;
-				for (int c = lhs + 1; c < smaller_idx_array.size(); ++c)
+				for (size_t c = lhs + 1; c < smaller_idx_array.size(); ++c)
 				{
 					size_t c_native_idx = smaller_idx_array[c];
 					const Scaffold& c_scaff = fragments[c_native_idx];
@@ -482,7 +468,7 @@ bool collapse_equivalent_transfrags(vector<Scaffold>& fragments,
 						{
 							if (num_merges % 100 == 0)
 							{
-								asm_verbose("%s\tCollapsing frag # %d\n", 
+								verbose_msg("%s\tCollapsing frag # %d\n", 
 										bundle_label->c_str(), 
 										num_merges);
 							}
@@ -527,25 +513,30 @@ bool collapse_equivalent_transfrags(vector<Scaffold>& fragments,
 					
 						// Now check that c doesn't have any additional conflicts
 						// of it's own
-						for (int i = lhs_native_idx + 1; i < fragments.size(); ++i)
+						for (size_t i = lhs_native_idx + 1; i < fragments.size(); ++i)
 						{
-							if (Scaffold::overlap_in_genome(fragments[i], lhs_scaff, 0))
-							{
-								if (Scaffold::overlap_in_genome(fragments[i], c_scaff, 0) &&
-									!Scaffold::compatible(fragments[i], c_scaff))
-								{
-									//c_conflicts.push_back(i);
-									if (!binary_search(curr_conflicts.begin(), curr_conflicts.end(), i))
-									{
-										not_equivalent = true;
-										break;
-									}
-								}
-							}
-							else
-							{
-								break;
-							}
+                            if (replacements[i] == i)
+                            {
+                                if (Scaffold::overlap_in_genome(fragments[i], lhs_scaff, 0))
+                                {
+                                    if (Scaffold::overlap_in_genome(fragments[i], c_scaff, 0))
+                                    {
+                                        if (!Scaffold::compatible(fragments[i], c_scaff))
+                                        {
+                                            //c_conflicts.push_back(i);
+                                            if (!binary_search(curr_conflicts.begin(), curr_conflicts.end(), i))
+                                            {
+                                                not_equivalent = true;
+                                                break;
+                                            }
+                                        }
+                                    }
+                                }
+                                else
+                                {
+                                    break;
+                                }
+                            }
 						}
 						
 						
@@ -555,7 +546,7 @@ bool collapse_equivalent_transfrags(vector<Scaffold>& fragments,
 						// merge
 						if (num_merges % 100 == 0)
 						{
-							asm_verbose("%s\tCollapsing frag # %d\n", 
+							verbose_msg("%s\tCollapsing frag # %d\n", 
 									bundle_label->c_str(), 
 									num_merges);
 						}
@@ -611,7 +602,7 @@ void compress_consitutive(vector<Scaffold>& hits)
 {
     vector<bool> scaffold_mask;
     
-    asm_verbose("%s\tBuilding constitutivity mask\n", bundle_label->c_str()); 
+    verbose_msg("%s\tBuilding constitutivity mask\n", bundle_label->c_str()); 
     
     scaffold_mask = vector<bool>(hits.size(), false);
     add_non_constitutive_to_scaffold_mask(hits, scaffold_mask);
@@ -639,12 +630,16 @@ void compress_consitutive(vector<Scaffold>& hits)
         hits.insert(hits.end(), completes.begin(), completes.end()); 
     }
     
+    fill_unambiguous_unknowns(non_constitutive, hits);
+    
     hits.insert(hits.end(), non_constitutive.begin(), non_constitutive.end());
     sort(hits.begin(), hits.end(), scaff_lt);
+    
+    
     size_t post_compress = hits.size();
     size_t delta = pre_compress - post_compress;
     double collapse_ratio = delta / (double) pre_compress; 
-    asm_verbose("%s\tCompressed %lu of %lu constitutive fragments (%lf percent)\n", 
+    verbose_msg("%s\tCompressed %lu of %lu constitutive fragments (%lf percent)\n", 
             bundle_label->c_str(),
             delta, 
             pre_compress, 
@@ -665,6 +660,8 @@ void compress_redundant(vector<Scaffold>& fragments)
         rightmost = std::max((long)fragments[i].right(), rightmost);
     }
     
+    size_t pre_compress = fragments.size();
+    
     while (true)
     {
         if (last_size == -1 || 0.9 * last_size > fragments.size())
@@ -680,22 +677,29 @@ void compress_redundant(vector<Scaffold>& fragments)
             break;
         }
     }
+    
+    size_t post_compress = fragments.size();
+    size_t delta = pre_compress - post_compress;
+    double collapse_ratio = delta / (double) pre_compress; 
+    verbose_msg("%s\tCompressed %lu of %lu redundant fragments (%lf percent)\n", 
+                bundle_label->c_str(),
+                delta, 
+                pre_compress, 
+                collapse_ratio);
 }
 
 void compress_fragments(vector<Scaffold>& fragments)
 {
-    asm_verbose("%s\tPerforming preliminary containment collapse on %lu fragments\n", bundle_label->c_str(), fragments.size());
+    verbose_msg("%s\tPerforming preliminary containment collapse on %lu fragments\n", bundle_label->c_str(), fragments.size());
     size_t pre_hit_collapse_size = fragments.size();
     sort(fragments.begin(), fragments.end(), scaff_lt_rt);
-	
-    fill_unambiguous_unknowns(fragments);
     
 	compress_consitutive(fragments);
 	
 	compress_redundant(fragments);
     
     size_t post_hit_collapse_size = fragments.size();
-    asm_verbose("%s\tIgnoring %lu strictly contained fragments\n", bundle_label->c_str(), pre_hit_collapse_size - post_hit_collapse_size);
+    verbose_msg("%s\tIgnoring %lu strictly contained fragments\n", bundle_label->c_str(), pre_hit_collapse_size - post_hit_collapse_size);
 }
 
 void compress_overlap_dag_paths(DAG& bundle_dag,
@@ -728,13 +732,13 @@ void compress_overlap_dag_paths(DAG& bundle_dag,
         if (!compressed_paths[i].empty())
         {
             Scaffold s(compressed_paths[i]);
-            asm_verbose("Path over %d-%d has %lu fragments in it\n", s.left(), s.right(), compressed_paths[i].size());
+            verbose_msg("Path over %d-%d has %lu fragments in it\n", s.left(), s.right(), compressed_paths[i].size());
             new_scaffs.push_back(s);
         }
     }
     //hits = new_scaffs;
 	
-    asm_verbose("%s\tCompressed overlap graph from %lu to %lu fragments (%f percent)\n",
+    verbose_msg("%s\tCompressed overlap graph from %lu to %lu fragments (%f percent)\n",
             bundle_label->c_str(), 
             hits.size(), 
             new_scaffs.size(), 
diff --git a/src/gtf_to_sam.cpp b/src/gtf_to_sam.cpp
index e7e1f72..7a69fe0 100644
--- a/src/gtf_to_sam.cpp
+++ b/src/gtf_to_sam.cpp
@@ -18,36 +18,46 @@
 #include <string>
 
 #include <boost/version.hpp>
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/depth_first_search.hpp>
+#include <boost/graph/visitors.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/connected_components.hpp>
 
 #include "common.h"
 #include "hits.h"
 #include "bundles.h"
 
-#include "gtf_tracking.h"
 #include "scaffolds.h"
+#include "tokenize.h"
 
+using namespace boost;
 using namespace std;
 
 #if ENABLE_THREADS
-const char *short_options = "r:";
+const char *short_options = "r:F";
 #else
-const char *short_options = "r:";
+const char *short_options = "r:F";
 #endif
 
+bool raw_fpkm = false;
+
 static struct option long_options[] = {
-{"reference-seq",		required_argument,		 0,			 'r'},	
+{"reference-seq",		required_argument,		 0,			 'r'},
+{"raw-fpkm",            no_argument,             0,			 'F'},
 {0, 0, 0, 0} // terminator
 };
 
 void print_usage()
 {
 	//NOTE: SPACES ONLY, bozo
-	fprintf(stderr, "cufflinks v%s\n", PACKAGE_VERSION);
+	fprintf(stderr, "gtf_to_sam v%s\n", PACKAGE_VERSION);
 	fprintf(stderr, "linked against Boost version %d\n", BOOST_VERSION);
 	fprintf(stderr, "-----------------------------\n"); 
-	fprintf(stderr, "Usage:   cufflinks [options] <transcripts.gtf> <out.sam\n");
+	fprintf(stderr, "Usage:   cufflinks [options] <transcripts1.gtf,...,transcriptsN.gtf> <out.sam>\n");
 	fprintf(stderr, "Options:\n\n");
-	fprintf(stderr, "-r/--reference-seq			  reference fasta file for sequence bias correction     [ default:   NULL ]\n");
+	fprintf(stderr, "-r/--reference-seq			  reference fasta file                     [ default:   NULL ]\n");
+    fprintf(stderr, "-F/--raw-fpkm			      use FPKM instead of isoform fraction                        \n");
 }
 
 int parse_options(int argc, char** argv)
@@ -64,6 +74,11 @@ int parse_options(int argc, char** argv)
 				fasta_dir = optarg;
 				break;
             }    
+            case 'F':
+			{
+				raw_fpkm = true;
+				break;
+            }   
 			default:
 				print_usage();
 				return 1;
@@ -160,24 +175,113 @@ void print_scaff_as_sam(FILE* sam_out,
 				"\tXS:A:%c",
 				scaff.strand() == CUFF_REV ? '-' : '+');
 	}
+    
+    if (scaff.fpkm() != 0)
+	{
+		fprintf(sam_out,
+				"\tZF:f:%f",
+				scaff.fpkm());
+	}
 	
 	fprintf(sam_out, "\n");
     
 }
 	
-void driver(FILE* ref_gtf, FILE* sam_out)
+void set_relative_fpkms(vector<shared_ptr<Scaffold> >& ref_mRNAs)
 {
-	ReadTable it;
-	RefSequenceTable rt(true, false);
+    adjacency_list <vecS, vecS, undirectedS> G;
 	
-	vector<shared_ptr<Scaffold> > ref_mRNAs;
+	for (size_t i = 0; i < ref_mRNAs.size(); ++i)
+	{
+		add_vertex(G);
+	}
 	
-	::load_ref_rnas(ref_gtf, rt, ref_mRNAs, false, false);
+    map<string, vector<int> > gene_id_idxs;
+    
+    for (size_t i = 0; i < ref_mRNAs.size(); ++i)
+    {
+        pair<map<string, vector<int> >::iterator, bool> inserted;
+        inserted = gene_id_idxs.insert(make_pair(ref_mRNAs[i]->annotated_gene_id(), vector<int>()));
+        inserted.first->second.push_back(i);
+    }
+    
+    for (map<string, vector<int> >::iterator itr = gene_id_idxs.begin();
+         itr != gene_id_idxs.end();
+         ++itr)
+    {
+        vector<int>& gene = itr->second;
+        for (size_t i = 0; i < gene.size(); ++i)
+        {
+            for (size_t j = 0; j < gene.size(); ++j)
+            {
+                {
+                    add_edge(gene[i], gene[j], G);
+                }
+            }
+        }
+    }
+    
+    std::vector<int> component(num_vertices(G));
+	connected_components(G, &component[0]);
+	
+	vector<vector<bool> > clusters(ref_mRNAs.size(), 
+								   vector<bool>(ref_mRNAs.size(), false));
 	
+	//vector<vector<size_t> > cluster_indices(three_prime_ends.size());
+    
+    vector<vector<shared_ptr<Scaffold> > > grouped_scaffolds(ref_mRNAs.size());
 	for (size_t i = 0; i < ref_mRNAs.size(); ++i)
 	{
-	    print_scaff_as_sam(sam_out, rt, *ref_mRNAs[i]);
+		clusters[component[i]][i] = true;
+		grouped_scaffolds[component[i]].push_back(ref_mRNAs[i]);
 	}
+    
+    for (size_t i = 0; i < grouped_scaffolds.size(); ++i)
+    {
+        vector<shared_ptr<Scaffold> >& gene = grouped_scaffolds[i];
+        
+        double total_fpkm = 0.0;
+        foreach(shared_ptr<Scaffold> scaff, gene)
+        {
+            total_fpkm += scaff->fpkm();
+        }
+        if (total_fpkm > 0)
+        {
+            foreach (shared_ptr<Scaffold> scaff, gene)
+            {
+                scaff->fpkm(scaff->fpkm() / total_fpkm);
+            }
+        }
+    }
+}
+
+void driver(vector<FILE*> ref_gtf_files, FILE* sam_out)
+{
+	ReadTable it;
+	RefSequenceTable rt(true, false);
+	
+	vector<vector<shared_ptr<Scaffold> > > ref_mRNA_table;
+	vector<pair<string, vector<double> > > sample_count_table;
+    
+    foreach (FILE* ref_gtf, ref_gtf_files)
+    {
+        vector<shared_ptr<Scaffold> > ref_mRNAs;
+        ::load_ref_rnas(ref_gtf, rt, ref_mRNAs, false, true);
+        ref_mRNA_table.push_back(ref_mRNAs);
+    }
+    
+    for (size_t j = 0; j < ref_mRNA_table.size(); ++j)
+    {
+        vector<shared_ptr<Scaffold> > ref_mRNAs = ref_mRNA_table[j];
+        
+        if (!raw_fpkm)
+            set_relative_fpkms(ref_mRNAs);
+        
+        for (size_t i = 0; i < ref_mRNAs.size(); ++i)
+        {
+            print_scaff_as_sam(sam_out, rt, *ref_mRNA_table[j][i]);
+        }
+    }
 }
 
 int main(int argc, char** argv)
@@ -195,7 +299,7 @@ int main(int argc, char** argv)
         return 1;
     }
 	
-    string ref_gtf_in_filename = argv[optind++];
+    string ref_gtf_in_filenames = argv[optind++];
     
     if(optind >= argc)
     {
@@ -205,17 +309,26 @@ int main(int argc, char** argv)
 	
     string sam_out_filename = argv[optind++];
     
-    FILE* ref_gtf = NULL;
-	if (ref_gtf_in_filename != "")
-	{
-		ref_gtf = fopen(ref_gtf_in_filename.c_str(), "r");
-		if (!ref_gtf)
-		{
-			fprintf(stderr, "Error: cannot open GTF file %s for reading\n",
-					ref_gtf_in_filename.c_str());
-			exit(1);
-		}
-	}
+    vector<string> ref_gtf_filenames;
+    tokenize(ref_gtf_in_filenames, ",", ref_gtf_filenames);
+    
+    vector<FILE*> ref_gtf_files;
+    
+    foreach (const string& ref_gtf_in_filename, ref_gtf_filenames)
+    {
+        FILE* ref_gtf = NULL;
+        if (ref_gtf_in_filename != "")
+        {
+            ref_gtf = fopen(ref_gtf_in_filename.c_str(), "r");
+            if (!ref_gtf)
+            {
+                fprintf(stderr, "Error: cannot open GTF file %s for reading\n",
+                        ref_gtf_in_filename.c_str());
+                exit(1);
+            }
+            ref_gtf_files.push_back(ref_gtf);
+        }
+    }
     
     FILE* sam_out = NULL;
 	if (sam_out_filename != "")
@@ -229,7 +342,7 @@ int main(int argc, char** argv)
 		}
 	}
     
-    driver(ref_gtf, sam_out);
+    driver(ref_gtf_files, sam_out);
 	
 	return 0;
 }
diff --git a/src/gtf_tracking.cpp b/src/gtf_tracking.cpp
index c23da30..a322f7a 100644
--- a/src/gtf_tracking.cpp
+++ b/src/gtf_tracking.cpp
@@ -7,27 +7,25 @@
  *
  */
 
-#include <cstdlib>
 #include "gtf_tracking.h"
 
-const char* ATTR_GENE_NAME=  "gene_name";
-
 bool gtf_tracking_verbose = false;
 bool gtf_tracking_largeScale=false; //many input Cufflinks files processed at once by cuffcompare, discard exon attributes
 
 int GXConsensus::count=0;
 
-int cmpByPtr(const pointer p1, const pointer p2) {
-  return (p1>p2) ? 1: ((p1==p2)? 0 : -1);
-  }
-
 char* getGSeqName(int gseq_id) {
  return GffObj::names->gseqs.getName(gseq_id);
 }
 
-GffObj* is_mRNADup(GffObj* m, GList<GffObj>& mrnas) {
+int cmpByPtr(const pointer p1, const pointer p2) {
+  return (p1>p2) ? 1: ((p1==p2)? 0 : -1);
+  }
+
+GffObj* is_RefDup(GffObj* m, GList<GffObj>& mrnas, int& dupidx) {
  //mrnas MUST be sorted by start coordinate
   int ovlen=0;
+  dupidx=-1;
   if (mrnas.Count()==0) return NULL;
   int nidx=qsearch_mrnas(m->end, mrnas);
   if (nidx==0) return NULL;
@@ -40,7 +38,10 @@ GffObj* is_mRNADup(GffObj* m, GList<GffObj>& mrnas) {
            }
       if (omrna.start>m->end) continue; //this should never be the case if nidx was found correctly
       //locus overlap here:
-      if (tMatch(*m, omrna, ovlen)) return mrnas[i];
+      if (tMatch(*m, omrna, ovlen)) {
+             dupidx=i;
+             return mrnas[i];
+             }
       }
   return NULL;
 }
@@ -55,33 +56,43 @@ bool intronRedundant(GffObj& ti, GffObj&  tj) {
  if (ti.exons[imax]->start<tj.exons[0]->end ||
      tj.exons[jmax]->start<ti.exons[0]->end )
          return false; //intron chains do not overlap at all
- //find the first intron overlap:
- uint istart=0, iend=0, jstart=0, jend=0;
+ 
+ uint eistart=0, eiend=0, ejstart=0, ejend=0; //exon boundaries
  int i=1; //exon idx to the right of the current intron of ti
  int j=1; //exon idx to the right of the current intron of tj
+ //find the first intron overlap:
  while (i<=imax && j<=jmax) {
-    istart=ti.exons[i-1]->end;
-    iend=ti.exons[i]->start;
-    jstart=tj.exons[j-1]->end;
-    jend=tj.exons[j]->start;
-    if (jend<istart) { j++; continue; }
-    if (iend<jstart) { i++; continue; }
+    eistart=ti.exons[i-1]->end;
+    eiend=ti.exons[i]->start;
+    ejstart=tj.exons[j-1]->end;
+    ejend=tj.exons[j]->start;
+    if (ejend<eistart) { j++; continue; }
+    if (eiend<ejstart) { i++; continue; }
     //we found an intron overlap
     break;
     }
  if ((i>1 && j>1) || i>imax || j>jmax) {
-     return false; //no intron overlaps found at all
-                  //or not the first intron for at least one of the transcripts
+     return false; //either no intron overlaps found at all
+                  //or it's not the first intron for at least one of the transcripts
      }
- if (istart!=jstart || iend!=jend) return false; //not an exact intron match
+ if (eistart!=ejstart || eiend!=ejend) return false; //not an exact intron match
  if (j>i) {
    //i==1, ti's start must not conflict with the previous intron of tj
    if (ti.start<tj.exons[j-1]->start) return false;
-   } else if (i>j) {
+   //so i's first intron starts AFTER j's first intron
+   // then j must contain i, so i's last intron must end with or before j's last intron
+   if (ti.exons[imax]->start>tj.exons[jmax]->start) return false;
+      //comment out the line above if you just want "intron compatibility" (i.e. extension of intron chains )
+   }
+  else if (i>j) {
    //j==1, tj's start must not conflict with the previous intron of ti
    if (tj.start<ti.exons[i-1]->start) return false;
+   //so j's intron chain starts AFTER i's
+   // then i must contain j, so j's last intron must end with or before j's last intron
+   if (tj.exons[jmax]->start>ti.exons[imax]->start) return false;
+      //comment out the line above for just "intronCompatible()" check
    }
- //now check if the rest of the introns overlap, in a linear succession
+ //now check if the rest of the introns overlap, in the same sequence
  i++;
  j++;
  while (i<=imax && j<=jmax) {
@@ -102,6 +113,24 @@ bool intronRedundant(GffObj& ti, GffObj&  tj) {
  return true;
 }
 
+bool t_contains(GffObj& a, GffObj& b) {
+ //returns true if b's intron chain (or single exon) is included in a
+ if (b.exons.Count()>=a.exons.Count()) return false;
+ if (b.exons.Count()==1) {
+    //check if b is contained in any of a's exons:
+    for (int i=0;i<a.exons.Count();i++) {
+       if (b.start>=a.exons[i]->start && b.end<=a.exons[i]->end) return true;
+       }
+    return false;
+    }
+ if (intronRedundant(a,b)) {
+    //intronRedudant allows b's initial/terminal exons to extend beyond a's boundaries
+    //but we don't allow this kind of behavior here
+    return (b.start>=a.start && b.end<=a.end);
+    }
+  else return false;
+ }
+
 int is_Redundant(GffObj*m, GList<GffObj>* mrnas) {
  //first locate the list index of the mrna starting just ABOVE
  //the end of this mrna
@@ -116,160 +145,171 @@ int is_Redundant(GffObj*m, GList<GffObj>* mrnas) {
           continue;
           }
      if (omrna.start>m->end) continue; //this should never be the case if nidx was found correctly
+     
      if (intronRedundant(*m, omrna)) return i;
      }
  return -1;
 }
 
-int parse_mRNAs(GList<GffObj>& mrnas,
+bool t_dominates(GffObj* a, GffObj* b) {
+ // for redundant / intron compatible transfrags:
+ // returns true if a "dominates" b, i.e. a has more exons or is longer
+ if (a->exons.Count()==b->exons.Count())
+         return (a->covlen>b->covlen);
+    else return (a->exons.Count()>b->exons.Count());
+}
+
+
+int parse_mRNAs(GfList& mrnas,
 				 GList<GSeqData>& glstdata,
 				 bool is_ref_set,
 				 bool check_for_dups,
 				 int qfidx, bool only_multiexon) {
 	int refdiscarded=0; //ref duplicates discarded
 	int tredundant=0; //cufflinks redundant transcripts discarded
-	int mrna_deleted=0;
 	for (int k=0;k<mrnas.Count();k++) {
 		GffObj* m=mrnas[k];
 		int i=-1;
 		GSeqData f(m->gseq_id);
 		GSeqData* gdata=NULL;
 		uint tlen=m->len();
-		if (m->hasErrors || (tlen+500>GFF_MAX_LOCUS)) { //should probably report these in a file too..
-			GMessage("Warning: transcript %s discarded (structural errors found, length=%d).\n", m->getID(), tlen);
-			mrnas.freeItem(k);
-			mrna_deleted++;
+		if (m->hasErrors() || (tlen+500>GFF_MAX_LOCUS)) { //should probably report these in a file too..
+			if (gtf_tracking_verbose) 
+			      GMessage("Warning: transcript %s discarded (structural errors found, length=%d).\n", m->getID(), tlen);
 			continue;
 			}
 		if (only_multiexon && m->exons.Count()<2) {
-			mrnas.freeItem(k);
-            mrna_deleted++;
 			continue;
 			}
-		GStr feature(m->getFeatureName());
-		feature.lower();
-		if (m->monoFeature() && (feature=="gene" || feature.index("loc")>=0)) {
+		//GStr feature(m->getFeatureName());
+		//feature.lower();
+		//bool gene_or_locus=(feature.endsWith("gene") ||feature.index("loc")>=0);
+		//if (m->exons.Count()==0 && gene_or_locus) {
+		if (m->isDiscarded()) {
 			//discard generic "gene" or "locus" features with no other detailed subfeatures
-			//GMessage("Warning: discarding %s GFF generic gene/locus container %s\n",m->getID());
-			mrnas.freeItem(k);
-            mrna_deleted++;
+			//if (gtf_tracking_verbose)
+			//   GMessage("Warning: discarding GFF generic gene/locus container %s\n",m->getID());
 			continue;
 			}
 		if (m->exons.Count()==0) {
-				//GMessage("Warning: %s %s found without exon segments; adjusting..\n",m->getFeatureName(), m->getID());
+				//if (gtf_tracking_verbose)
+				// GMessage("Warning: %s %s found without exon segments (adding default exon).\n",m->getFeatureName(), m->getID());
 				m->addExon(m->start,m->end);
 				}
 		if (glstdata.Found(&f,i)) gdata=glstdata[i];
 		else {
 			gdata=new GSeqData(m->gseq_id);
 			glstdata.Add(gdata);
-		}
-
+			}
+		
 		double fpkm=0;
 		double cov=0;
 		double conf_hi=0;
 		double conf_lo=0;
-		if (is_ref_set) {
-		  if (check_for_dups) {
-		 //check all gdata->mrnas_r (ref_data) for duplicate ref transcripts
-		  GffObj* rp= (m->strand=='+') ? is_mRNADup(m, gdata->mrnas_f) :
-		                                 is_mRNADup(m, gdata->mrnas_r);
-		  if (rp!=NULL) {
-		    //just discard this, it's a duplicate ref
-		     //but let's just keep the gene_name if present
-		     char* gname=m->getAttr(ATTR_GENE_NAME);
-		     char* pgname=rp->getAttr(ATTR_GENE_NAME);
-		     if (pgname==NULL && gname!=NULL)
-		         rp->addAttr(ATTR_GENE_NAME, gname);
-			 //GMessage("--------->  ref %s as a duplicate of %s\n",m->getID(), rp->getID());
-		     mrnas.freeItem(k);
-		     mrna_deleted++;
-		     refdiscarded++;
+
+		GList<GffObj>* target_mrnas=NULL;
+		if (is_ref_set) { //-- ref transcripts
+		   if (m->strand=='.') {
+		     //unknown strand - discard from reference set (!)
 		     continue;
 		     }
-		   } //suppress ref dups
-		} //is ref 
-		else { // Cufflinks gtf
-		if (m->strand!='.' && check_for_dups) { //oriented transfrag
-			// check if there is a redundancy between this and another already loaded Cufflinks transcript
-			GList<GffObj>* ckmrnas=(m->strand=='+') ? &(gdata->mrnas_f) : &(gdata->mrnas_r);
-			int cidx =  is_Redundant(m, ckmrnas);
-			if (cidx>=0) {
-				//always discard the "shorter" transcript of the redundant pair
-				if (ckmrnas->Get(cidx)->covlen>m->covlen) {
-				//new transcript is shorter, discard it
-					mrnas.freeItem(k);
-					mrna_deleted++;
-					continue;
-				} else {
-					//new transcript is longer, discard the older one
-					((CTData*)(ckmrnas->Get(cidx)->uptr))->mrna=NULL;
-					ckmrnas->Delete(cidx);
-				//the uptr (CTData) pointer will still be kept in gdata->ctdata and freed accordindly at the end
-					}
-				tredundant++;
-				}
-			// ^^^ redundant transcript check
-			} //oriented transfrag
-		if (m->gscore==0.0)   
-		   m->gscore=m->exons[0]->score; //Cufflinks exon score = isoform abundance
-		//for Cufflinks file, parse expr attribute from the 1st exon (the lowest coordinate exon)
-		const char* expr = (gtf_tracking_largeScale) ? m->getAttr("FPKM") : m->exons[0]->getAttr(m->names,"FPKM");
-		if (expr!=NULL) {
-			if (expr[0]=='"') expr++;
-			fpkm=strtod(expr, NULL);
-			} else { //backward compatibility: read RPKM if FPKM not found
-			expr=(gtf_tracking_largeScale) ? m->getAttr("RPKM") : m->exons[0]->getAttr(m->names,"RPKM");
-			if (expr!=NULL) {
-				if (expr[0]=='"') expr++;
-				fpkm=strtod(expr, NULL);
-				}
-			}
-		const char* scov=(gtf_tracking_largeScale) ? m->getAttr("cov") : m->exons[0]->getAttr(m->names,"cov");
-		if (scov!=NULL) {
-			if (scov[0]=='"') scov++; 
-			cov=strtod(scov, NULL);
-			}
-		const char* sconf_hi=(gtf_tracking_largeScale) ? m->getAttr("conf_hi") : m->exons[0]->getAttr(m->names,"conf_hi");
-		if (sconf_hi!=NULL){
-			if (sconf_hi[0]=='"') sconf_hi++;
-			conf_hi=strtod(sconf_hi, NULL);
-			}
-		const char* sconf_lo=(gtf_tracking_largeScale) ? m->getAttr("conf_lo") : m->exons[0]->getAttr(m->names,"conf_lo");
-		if (sconf_lo!=NULL) {
-			if (sconf_lo[0]=='"') sconf_lo++;
-			conf_lo=strtod(sconf_lo, NULL);
-			}
-		} //Cufflinks transfrags
-
-		if (m->strand=='+') gdata->mrnas_f.Add(m);
-		   else {
-			 if (m->strand=='-') gdata->mrnas_r.Add(m);
-			 else { //unknown strand, unoriented mRNA
-				 if (is_ref_set) {// discard these from reference
-					mrnas.freeItem(k);
-					mrna_deleted++;
-					continue;
-					}
-				else {//store them in the unknown strand pile, to be analyzed later
-					m->strand=0;
-					gdata->umrnas.Add(m);
-					}
-				} //unknown strand
-			} //- or unknown strand
+		   if (check_for_dups) {
+		     //check all gdata->mrnas_r (ref_data) for duplicate ref transcripts
+		     target_mrnas=(m->strand=='+') ? &(gdata->mrnas_f) : &(gdata->mrnas_r);
+		     int rpidx=-1;
+		     GffObj* rp= is_RefDup(m, *target_mrnas, rpidx);
+		     if (rp!=NULL) { //duplicate found
+		       //discard the one that was seen "later" (higher track_id)
+		       //but let's keep the gene_name if present
+		       refdiscarded++;
+		       if (rp->track_id <= m->track_id) {
+		           if (rp->getGeneName()==NULL && m->getGeneName()!=NULL) {
+		                  rp->setGeneName(m->getGeneName());
+		                  }
+		           continue;
+		           }
+		         else {
+		           if (m->getGeneName()==NULL && rp->getGeneName()!=NULL) {
+		                  m->setGeneName(rp->getGeneName());
+		                  }
+		           ((CTData*)(rp->uptr))->mrna=NULL;
+		           rp->isUsed(false);
+		           target_mrnas->Forget(rpidx);
+		           target_mrnas->Delete(rpidx);
+		           }
+		       }
+		     } //check for duplicate ref transcripts
+		   } //ref transcripts
+		else { //-- transfrags
+		   if (m->strand=='+') { target_mrnas = &(gdata->mrnas_f); }
+		     else if (m->strand=='-') { target_mrnas=&(gdata->mrnas_r); }
+		        else { m->strand='.'; target_mrnas=&(gdata->umrnas); }
+		   if (check_for_dups) { //check for redundancy
+		     // check if there is a redundancy between this and another already loaded Cufflinks transcript
+		     int cidx =  is_Redundant(m, target_mrnas);
+		     if (cidx>=0) {
+		        //always discard the redundant transcript with the fewer exons OR shorter
+		        if (t_dominates(target_mrnas->Get(cidx),m)) {
+		            //new transcript is shorter, discard it
+		            continue;
+		            } 
+		        else {
+		            //discard the older transfrag
+		            ((CTData*)(target_mrnas->Get(cidx)->uptr))->mrna=NULL;
+		            target_mrnas->Get(cidx)->isUsed(false);
+		            target_mrnas->Forget(cidx);
+		            target_mrnas->Delete(cidx);
+		            //the uptr (CTData) pointer will still be kept in gdata->ctdata and deallocated eventually
+		            }
+		        tredundant++;
+		        }
+		     }// redundant transfrag check
+		   if (m->gscore==0.0)   
+		     m->gscore=m->exons[0]->score; //Cufflinks exon score = isoform abundance
+		   //parse attributes from the 1st exon
+		   const char* expr = (gtf_tracking_largeScale) ? m->getAttr("FPKM") : m->exons[0]->getAttr(m->names,"FPKM");
+		   if (expr!=NULL) {
+		       if (expr[0]=='"') expr++;
+		       fpkm=strtod(expr, NULL);
+		       } else { //backward compatibility: read RPKM if FPKM not found
+		       expr=(gtf_tracking_largeScale) ? m->getAttr("RPKM") : m->exons[0]->getAttr(m->names,"RPKM");
+		       if (expr!=NULL) {
+		           if (expr[0]=='"') expr++;
+		           fpkm=strtod(expr, NULL);
+		           }
+		       }
+		   const char* scov=(gtf_tracking_largeScale) ? m->getAttr("cov") : m->exons[0]->getAttr(m->names,"cov");
+		   if (scov!=NULL) {
+		       if (scov[0]=='"') scov++; 
+		       cov=strtod(scov, NULL);
+		       }
+		   const char* sconf_hi=(gtf_tracking_largeScale) ? m->getAttr("conf_hi") : m->exons[0]->getAttr(m->names,"conf_hi");
+		   if (sconf_hi!=NULL){
+		       if (sconf_hi[0]=='"') sconf_hi++;
+		       conf_hi=strtod(sconf_hi, NULL);
+		       }
+		   const char* sconf_lo=(gtf_tracking_largeScale) ? m->getAttr("conf_lo") : m->exons[0]->getAttr(m->names,"conf_lo");
+		   if (sconf_lo!=NULL) {
+		       if (sconf_lo[0]=='"') sconf_lo++;
+		       conf_lo=strtod(sconf_lo, NULL);
+		       }
+		   } //Cufflinks transfrags
+		target_mrnas->Add(m);
+		m->isUsed(true);
 		CTData* mdata=new CTData(m);
 		mdata->qset=qfidx;
 		gdata->tdata.Add(mdata);
+		if (!is_ref_set) {
 		// Cufflinks - attributes parsing
-		mdata->FPKM=fpkm;
-		mdata->cov=cov;
-		mdata->conf_hi=conf_hi;
-		mdata->conf_lo=conf_lo;
-		//
+		   mdata->FPKM=fpkm;
+		   mdata->cov=cov;
+		   mdata->conf_hi=conf_hi;
+		   mdata->conf_lo=conf_lo;
+		   }
 	}//for each mrna read
- if (mrna_deleted>0) {
-   mrnas.Pack();
-   }
+ //if (mrna_deleted>0)
+ //  mrnas.Pack();
+ 
  return (is_ref_set ? refdiscarded : tredundant);
 }
 
@@ -280,18 +320,19 @@ bool tMatch(GffObj& a, GffObj& b, int& ovlen, bool equnspl) {
 	ovlen=0;
 	if (imax!=jmax) return false; //different number of introns
 	if (imax==0) { //single-exon mRNAs
-		//consider match if they overlap over 50% of max len
 		if (equnspl) {
+			//fuzz match for single-exon transfrags: 
+			// it's a match if they overlap at least 80% of max len
 			ovlen=a.exons[0]->overlapLen(b.exons[0]);
 			int maxlen=GMAX(a.covlen,b.covlen);
-			return (ovlen>maxlen/2);
+			return (ovlen>=maxlen*0.8);
 		}
-        else {
-            //only exact match
+	  else {
+			//only exact match
 			ovlen=a.covlen;
 			return (a.exons[0]->start==b.exons[0]->start &&
 					a.exons[0]->end==b.exons[0]->end);
-        }
+		}
 	}
 	if ( a.exons[imax]->start<b.exons[0]->end ||
 		b.exons[jmax]->start<a.exons[0]->end )
@@ -306,15 +347,14 @@ bool tMatch(GffObj& a, GffObj& b, int& ovlen, bool equnspl) {
             return false; //intron mismatch
 		}
 	}
-	//GMessage("tMatch found: %s == %s\n", a.getID(),b.getID());
 	return true;
 }
 
-void cluster_mRNAs(GList<GffObj> & mrnas, GList<GLocus> & loci, int qfidx, bool refData) {
+void cluster_mRNAs(GList<GffObj> & mrnas, GList<GLocus> & loci, int qfidx) {
 	//mrnas sorted by start coordinate
 	//and so are the loci
 	//int rdisc=0;
-   	for (int t=0;t<mrnas.Count();t++) {
+		for (int t=0;t<mrnas.Count();t++) {
 		GArray<int> mrgloci(false);
 		GffObj* mrna=mrnas[t];
 		int lfound=0; //count of parent loci
@@ -340,8 +380,7 @@ void cluster_mRNAs(GList<GffObj> & mrnas, GList<GLocus> & loci, int qfidx, bool
  			 loci.Add(new GLocus(mrna, qfidx));
 		    }
 		 else if (lfound>1) {
-			//more than one loci found parenting this mRNA, merge loci
-			//if (lfound>2) GMessage(" merging %d loci \n",lfound);
+			//more than one locus found parenting this mRNA, merge loci
 		     lfound--;
 			 for (int l=0;l<lfound;l++) {
 				  int mlidx=mrgloci[l]; //largest indices first, so it's safe to remove
@@ -362,7 +401,7 @@ int fix_umrnas(GSeqData& seqdata, GSeqData* rdata, FILE* fdis=NULL) {
 		for (int i=0;i<rdata->mrnas_f.Count();i++) {
 			for (int j=0;j<seqdata.umrnas.Count();j++) {
 				if (rdata->mrnas_f[i]->gseq_id!=seqdata.umrnas[j]->gseq_id) continue;
-				if (seqdata.umrnas[j]->strand>0) continue;
+				if (seqdata.umrnas[j]->strand!='.') continue;
 				uint ustart=seqdata.umrnas[j]->exons.First()->start;
 				uint uend=seqdata.umrnas[j]->exons.Last()->end;
 				uint rstart=rdata->mrnas_f[i]->exons.First()->start;
@@ -437,14 +476,14 @@ int fix_umrnas(GSeqData& seqdata, GSeqData* rdata, FILE* fdis=NULL) {
 			seqdata.umrnas.Forget(i);
 		}
 		else if (seqdata.umrnas[i]->strand=='-') {
-            seqdata.mrnas_r.Add(seqdata.umrnas[i]);
-            seqdata.umrnas.Forget(i);
+		    seqdata.mrnas_r.Add(seqdata.umrnas[i]);
+		    seqdata.umrnas.Forget(i);
 		}
 		else {  //discard mRNAs not settled
 			seqdata.umrnas[i]->strand='.';
 			if (fdis!=NULL) {
 				seqdata.umrnas[i]->printGtf(fdis);
-            }
+				}
 			fcount++;
 		}
 	}
@@ -462,29 +501,31 @@ GSeqData* getRefData(int gid, GList<GSeqData>& ref_data) {
 	return r;
 }
 
-void read_transcripts(FILE* f, GList<GSeqData>& seqdata) {
+void read_transcripts(FILE* f, GList<GSeqData>& seqdata, bool keepAttrs) {
 	rewind(f);
-	GffReader* gffr=new GffReader(f, false); //allow loading of non-mRNA transcripts also
+	GffReader gffr(f, true); //loading only recognizable transcript features
+	gffr.showWarnings(gtf_tracking_verbose);
+
 	//          keepAttrs   mergeCloseExons   noExonAttrs
-    gffr->showWarnings();
-	gffr->readAll(true,          true,        true);
+	gffr.readAll(keepAttrs,          true,        true);
+
 	//                               is_ref?    check_for_dups,
-	parse_mRNAs(gffr->gflst, seqdata, false,       false);
-	delete gffr;
+	parse_mRNAs(gffr.gflst, seqdata, false,       false);
 }
 
 
 void read_mRNAs(FILE* f, GList<GSeqData>& seqdata, GList<GSeqData>* ref_data,
-	         bool check_for_dups, int qfidx, const char* fname, bool checkseq, bool only_multiexon) {
+	         bool check_for_dups, int qfidx, const char* fname, bool only_multiexon) {
 	//>>>>> read all transcripts/features from a GTF/GFF3 file
 	//int imrna_counter=0;
 	int loci_counter=0;
 	if (ref_data==NULL) ref_data=&seqdata;
 	bool isRefData=(&seqdata==ref_data);
-	//GffReader* gffr=new GffReader(f, true); //(file, mRNA_only)
-	GffReader* gffr=new GffReader(f, !isRefData); //also consider non-mRNA annotations
-	//           keepAttrs   mergeCloseExons   noExonAttrs
-	gffr->readAll(true,          true,        isRefData || gtf_tracking_largeScale);
+	                          //(f, transcripts_only)
+	GffReader* gffr=new GffReader(f, true); //load only transcript annotations
+	gffr->showWarnings(gtf_tracking_verbose);
+	//            keepAttrs   mergeCloseExons   noExonAttrs
+	gffr->readAll(!isRefData,          true,        isRefData || gtf_tracking_largeScale);
 	//so it will read exon attributes only for low number of Cufflinks files
 	
 	int d=parse_mRNAs(gffr->gflst, seqdata, isRefData, check_for_dups, qfidx,only_multiexon);
@@ -493,7 +534,7 @@ void read_mRNAs(FILE* f, GList<GSeqData>& seqdata, GList<GSeqData>* ref_data,
 	             else GMessage(" %d redundant cufflinks transfrags discarded.\n",d);
 	  }
 	//imrna_counter=gffr->mrnas.Count();
-	delete gffr; //free the extra memory
+	delete gffr; //free the extra memory and unused GffObjs
 	
 	//for each genomic sequence, cluster transcripts
 	int discarded=0;
@@ -512,18 +553,17 @@ void read_mRNAs(FILE* f, GList<GSeqData>& seqdata, GList<GSeqData>* ref_data,
 	for (int g=0;g<seqdata.Count();g++) {
 		//find the corresponding refseqdata with the same gseq_id
 		int gseq_id=seqdata[g]->gseq_id;
-		//if (gtf_tracking_verbose) GMessage("Clustering mRNAs on %s...\n", getGSeqName(gseq_id));
 		if (!isRefData) { //cufflinks data, find corresponding ref data
 			GSeqData* rdata=getRefData(gseq_id, *ref_data);
 			if (rdata!=NULL && seqdata[g]->umrnas.Count()>0) {
-				discarded+=fix_umrnas(*seqdata[g], rdata, fdis);
+			    discarded+=fix_umrnas(*seqdata[g], rdata, fdis);
 			    }
-		    }
+			}
 		//>>>>> group mRNAs into locus-clusters (based on exon overlap)
-		cluster_mRNAs(seqdata[g]->mrnas_f, seqdata[g]->loci_f, qfidx, isRefData);
-		cluster_mRNAs(seqdata[g]->mrnas_r, seqdata[g]->loci_r, qfidx, isRefData);
+		cluster_mRNAs(seqdata[g]->mrnas_f, seqdata[g]->loci_f, qfidx);
+		cluster_mRNAs(seqdata[g]->mrnas_r, seqdata[g]->loci_r, qfidx);
 		if (!isRefData) {
-			cluster_mRNAs(seqdata[g]->umrnas, seqdata[g]->nloci_u, qfidx, false);
+			cluster_mRNAs(seqdata[g]->umrnas, seqdata[g]->nloci_u, qfidx);
 			}
 		loci_counter+=seqdata[g]->loci_f.Count();
 		loci_counter+=seqdata[g]->loci_r.Count();
@@ -540,7 +580,7 @@ void read_mRNAs(FILE* f, GList<GSeqData>& seqdata, GList<GSeqData>* ref_data,
 	if (fdis!=NULL) fclose(fdis);
 	if (frloci!=NULL) fclose(frloci);
 	if (discarded>0) {
-		GMessage("Warning: found %d transcripts with undetermined strand.\n", discarded);
+		if (gtf_tracking_verbose) GMessage("Found %d transcripts with undetermined strand.\n", discarded);
 	}
 	else { if (fdis!=NULL) remove(s.chars()); }
 }
@@ -578,26 +618,26 @@ int qsearch_mrnas(uint x, GList<GffObj>& mrnas) {
  return (idx>maxh) ? -1 : idx;
 }
 
-int qsearch_segs(uint x, GList<GSeg>& segs) {
+int qsearch_loci(uint x, GList<GLocus>& loci) {
  // same as above, but for GSeg lists
   //binary search
   //do the simplest tests first:
-  if (segs[0]->start>x) return 0;
-  if (segs.Last()->start<x) return -1;
+  if (loci[0]->start>x) return 0;
+  if (loci.Last()->start<x) return -1;
   uint istart=0;
   int i=0;
   int idx=-1;
-  int maxh=segs.Count()-1;
+  int maxh=loci.Count()-1;
   int l=0;
   int h = maxh;
   while (l <= h) {
      i = (l + h) >> 1;
-     istart=segs[i]->start;
+     istart=loci[i]->start;
      if (istart < x) l=i+1;
                 else {
                    if (istart == x) { //found matching coordinate here
                         idx=i;
-                        while (idx<=maxh && segs[idx]->start==x) {
+                        while (idx<=maxh && loci[idx]->start==x) {
                            idx++;
                            }
                         return (idx>maxh) ? -1 : idx;
@@ -606,7 +646,7 @@ int qsearch_segs(uint x, GList<GSeg>& segs) {
                    }
      } //while
  idx = l;
- while (idx<=maxh && segs[idx]->start<=x) {
+ while (idx<=maxh && loci[idx]->start<=x) {
     idx++;
     }
  return (idx>maxh) ? -1 : idx;
diff --git a/src/gtf_tracking.h b/src/gtf_tracking.h
index 1615cbf..d38df60 100644
--- a/src/gtf_tracking.h
+++ b/src/gtf_tracking.h
@@ -16,7 +16,6 @@
 
 
 #define MAX_QFILES 500
-extern const char* ATTR_GENE_NAME;
 
 extern bool gtf_tracking_verbose;
 
@@ -26,6 +25,9 @@ extern bool gtf_tracking_largeScale;
 
 int cmpByPtr(const pointer p1, const pointer p2);
 
+bool t_contains(GffObj& a, GffObj& b); 
+//returns true only IF b has fewer exons than a AND a "contains" b
+
 char* getGSeqName(int gseq_id);
 
 //genomic fasta sequence handling
@@ -74,7 +76,7 @@ class GFastaHandler {
             faIdx=new GFastaIndex(fastaPath,fainame.chars());
             GStr fainamecwd(fainame);
             int ip=-1;
-            if ((ip=fainamecwd.rindex(CHPATHSEP))>=0)
+            if ((ip=fainamecwd.rindex('/'))>=0)
                fainamecwd.cut(0,ip+1);
             if (!faIdx->hasIndex()) { //could not load index
                //try current directory
@@ -143,22 +145,22 @@ class GLocus;
 
 class COvLink {
 public:
-    static int coderank(char c) {
+	static int coderank(char c) {
 		switch (c) {
 			case '=': return 0; //ichain match
-			case 'c': return 2; //containment (chain fragment)
-			case 'e': return 4; // overlap exon+intron of unspliced pre-mRNA
-			case 'j': return 6; // overlap with at least a junction match
-			case 'o': return 8; // overlap (exon)
-			case 'r': return 14; //repeats
-			case 'i': return 16; // intra-intron
-            case 'x': return 18; // plain exon overlap on opposite strand
-			case 'p': return 20; //polymerase run
-			case 'u': return 90; //intergenic
-            case 's': return 94; //"shadow" - at least one (fuzzy) intron overlap on the opposite strand
+			case 'c': return 2; //containment (ichain fragment)
+			case 'j': return 4; // overlap with at least a junction match
+			case 'e': return 6; // single exon transfrag overlapping an intron of reference (possible pre-mRNA)
+			case 'o': return 8; // generic exon overlap
+			case 's': return 16; //"shadow" - an intron overlaps with a ref intron on the opposite strand
+			case 'x': return 18; // exon overlap on opposite strand (usually wrong strand mapping)
+			case 'i': return 20; // intra-intron
+			case 'p': return 90; //polymerase run
+			case 'r': return 92; //repeats
+			case 'u': return 94; //intergenic
 			case  0 : return 100;
 			default: return 96;
-        }
+			}
 	}
     char code;
     int rank;
@@ -213,16 +215,25 @@ class GIArray:public GArray<GISeg> {
 
 };
 
+class CEqList: public GList<GffObj> {
+  public:
+    GffObj* head;
+    CEqList():GList<GffObj>((GCompareProc*)cmpByPtr, (GFreeProc*)NULL, true) {
+      head=NULL;
+      }
+};
+
 class CTData { //transcript associated data
 public:
-	GffObj* mrna;
+	GffObj* mrna; //owner transcript
 	GLocus* locus;
 	GList<COvLink> ovls; //overlaps with other transcripts (ref vs query)
 	//-- just for ichain match tracking:
-	GffObj* eqref; //ref having an ichain match
-	int qset; //qry set index (qfidx)
-	GffObj* eqnext;
-	int eqdata;
+	GffObj* eqref; //ref transcript having an ichain match
+	int qset; //qry set index (qfidx), -1 means reference dataset
+	//GffObj* eqnext; //next GffObj in the linked list of matching transfrags
+	CEqList* eqlist; //keep track of matching transfrags
+	//int eqdata; // flags for EQ list (is it a list head?)
 	// Cufflinks specific data:
 	double FPKM;
 	double conf_hi;
@@ -233,19 +244,89 @@ public:
 		mrna=m;
 		if (mrna!=NULL) mrna->uptr=this;
 		locus=l;
-        classcode=0;
+		classcode=0;
 		eqref=NULL;
-		eqnext=NULL;
-		eqdata=0;
+		//eqnext=NULL;
+		eqlist=NULL;
+		//eqdata=0;
 		qset=-2;
 		FPKM=0;
 		conf_lo=0;
 		conf_hi=0;
 		cov=0;
 	}
+
 	~CTData() {
 		ovls.Clear();
+		//if ((eqdata & EQHEAD_TAG)!=0) delete eqlist;
+		if (isEqHead()) delete eqlist;
 	}
+
+  //inline bool eqHead() { return ((eqdata & EQHEAD_TAG)!=0); }
+  bool isEqHead() { 
+      if (eqlist==NULL) return false;
+      return (eqlist->head==this->mrna);
+      }
+    
+  void joinEqList(GffObj* m) { //add list from m
+   //list head is set to the transfrag with the lower qset#
+  CTData* md=(CTData*)(m->uptr);
+  //ASSERT(md);
+  if (eqlist==NULL) {
+     if (md->eqlist!=NULL) {
+          eqlist=md->eqlist;
+          eqlist->Add(this->mrna);
+          CTData* md_head_d=(CTData*)(md->eqlist->head->uptr);
+          if (this->qset < md_head_d->qset)
+               eqlist->head=this->mrna;
+          }
+        else { //m was not in an EQ list
+          //eqlist=new GList<GffObj>((GCompareProc*)cmpByPtr, (GFreeProc*)NULL, true);
+          eqlist=new CEqList();
+          eqlist->Add(this->mrna);
+          eqlist->Add(m);
+          md->eqlist=eqlist;
+          if (qset<md->qset) eqlist->head=this->mrna;
+                       else  eqlist->head=m;
+          }
+      }//no eqlist before
+     else { //merge two eqlists
+      if (eqlist==md->eqlist) //already in the same eqlist, nothing to do
+         return;
+      if (md->eqlist!=NULL) { //copy elements of m's eqlist
+        //copy the smaller list into the larger one
+        CEqList* srclst, *destlst;
+        if (md->eqlist->Count()<eqlist->Count()) {
+           srclst=md->eqlist;
+           destlst=eqlist;
+           }
+         else {
+           srclst=eqlist;
+           destlst=md->eqlist;
+           }
+         for (int i=0;i<srclst->Count();i++) {
+           destlst->Add(srclst->Get(i));
+           CTData* od=(CTData*)((*srclst)[i]->uptr);
+           od->eqlist=destlst;
+           //od->eqdata=od->qset+1;
+           }
+        this->eqlist=destlst;
+        CTData* s_head_d=(CTData*)(srclst->head->uptr);
+        CTData* d_head_d=(CTData*)(destlst->head->uptr);
+        if (s_head_d->qset < d_head_d->qset )
+             this->eqlist->head=srclst->head; 
+        delete srclst;
+        }
+       else { //md->eqlist==NULL
+        eqlist->Add(m);
+        md->eqlist=eqlist;
+        CTData* head_d=(CTData*)(eqlist->head->uptr);
+        if (md->qset<head_d->qset)
+            eqlist->head=m;
+        }
+      }
+  }
+
 	void addOvl(char code,GffObj* target=NULL, int ovlen=0) {
 		ovls.AddIfNew(new COvLink(code, target, ovlen));
 	}
@@ -958,19 +1039,21 @@ class GXConsensus:public GSeg {
    int id; //XConsensus ID
    int tss_id; //group id for those xconsensi with shared first exon
    int p_id; //group id for those xconsensi with "similar" protein
-   GffObj* tcons; //chosen (longest) transcript to define the "consensus"
+   GffObj* tcons; //longest transcript to represent the combined "consensus" structure
    GffObj* ref; //overlapping reference transcript 
    char refcode; // the code for ref relationship (like in the tracking file)
    char* aa;
    int aalen;
+   GXConsensus* contained; //if contained into another GXConsensus 
    //list of ichain-matching query (cufflinks) transcripts that contributed to this consensus
    GList<GffObj> qchain;
-   GXConsensus(GffObj* c, GList<GffObj> qlst, GffObj* r=NULL, char rcode=0)
+   GXConsensus(GffObj* c, CEqList* qlst, GffObj* r=NULL, char rcode=0)
                    :qchain(false,false,false) {
       ref=r;
       refcode=rcode;
       tcons=c;
-      qchain.Add(qlst);
+      if (qlst!=NULL) qchain.Add(*((GList<GffObj>*)qlst));
+                 else qchain.Add(c);
       count++;
       tss_id=0;
       p_id=0;
@@ -979,6 +1062,7 @@ class GXConsensus:public GSeg {
       aa=NULL;
       start=tcons->start;
       end=tcons->end;
+      contained=NULL;
       }
    ~GXConsensus() {
      if (aa!=NULL) GFREE(aa);
@@ -1057,7 +1141,7 @@ class GXLocus:public GSeg {
           j++; //check the next locus.mexon
         }//while mexons
       if (ovlexons.Count()==0) return false;
-      if (strand=='.' && loc->mrna_maxcov->strand>0)
+      if (strand=='.' && loc->mrna_maxcov->strand!='.')
              strand=loc->mrna_maxcov->strand;
       //have exon overlap:
       //-- add the rest of the non-overlapping mexons:
@@ -1131,12 +1215,64 @@ class GXLocus:public GSeg {
          rloci.Add(oxloc.rloci[i]);
          oxloc.rloci[i]->xlocus=this;
          }
+  } //::addMerge()
+
+
+ void checkContainment() {
+   //checking containment
+  for (int j=0;j<tcons.Count()-1;j++) {
+    GXConsensus* t=tcons[j];
+    for (int i=j+1;i<tcons.Count();i++) {
+       if (tcons[i]->contained!=NULL && t->tcons->exons.Count()>1) continue; //will check the container later anyway
+       int c_status=checkXConsContain(t->tcons, tcons[i]->tcons);
+       if (c_status==0) continue; //no containment relationship between t and tcons[i]
+       if (c_status>0) { //t is a container for tcons[i]
+            tcons[i]->contained=t;
+            }
+         else { //contained into exising XCons
+            t->contained=tcons[i];
+            break;
+            }
+       }
+   }
   }
-};
+ 
+ int checkXConsContain(GffObj* a, GffObj* b) {
+  // returns  1 if a is the container of b
+  //         -1 if a is contained in b
+  //          0 if no 
+  if (a->end<b->start || b->end<a->start) return 0;
+  if (a->exons.Count()==b->exons.Count()) {
+     if (a->exons.Count()>1) return 0; //same number of exons - no containment possible
+                                       //because equivalence was already tested
+           else { //single exon containment testing
+             //this is fuzzy and messy (end result may vary depending on the testing order)
+             int ovlen=a->exons[0]->overlapLen(b->exons[0]);
+             int minlen=GMIN(a->covlen, b->covlen);
+             if (ovlen>=minlen*0.8) { //if at least 80% of the shorter one is covered, it is contained
+                return ((a->covlen>b->covlen) ? 1 : -1);
+                }
+              else return 0;
+             //if (a->start<=b->start+10 && a->end+10>=b->end) return 1;
+             //  else { if (b->start<=a->start+10 && b->end+10>=a->end) return -1;
+             //          else return 0; 
+             //}
+             }
+     }
+   //different number of exons:
+   if (a->exons.Count()>b->exons.Count()) return t_contains(*a, *b) ? 1:0;
+                     else return t_contains(*b, *a) ? -1 : 0;
+  }
+  
+ void addXCons(GXConsensus* t) {
+  tcons.Add(t);
+  }
+  
+}; //GXLocus
 
 
 
-int parse_mRNAs(GList<GffObj>& mrnas,
+int parse_mRNAs(GfList& mrnas,
 				 GList<GSeqData>& glstdata,
 				 bool is_ref_set=true,
 				 bool check_for_dups=false,
@@ -1145,18 +1281,18 @@ int parse_mRNAs(GList<GffObj>& mrnas,
 //reading a mRNAs from a gff file and grouping them into loci
 void read_mRNAs(FILE* f, GList<GSeqData>& seqdata, GList<GSeqData>* ref_data=NULL, 
               bool check_for_dups=false, int qfidx=-1, const char* fname=NULL, 
-              bool checkseq=false, bool only_multiexon=false);
+              bool only_multiexon=false);
 
-void read_transcripts(FILE* f, GList<GSeqData>& seqdata);
+void read_transcripts(FILE* f, GList<GSeqData>& seqdata, bool keepAttrs=true);
 
 bool tMatch(GffObj& a, GffObj& b, int& ovlen, bool equnspl=false);
 
-//"position" a given coordinate x within a list of transcripts sorted by their start (lowest)
-//coordinate, using quick-search; the returned int is the list index of the closest *higher*
-//GffObj - i.e. starting right *ABOVE* the given coordinate
-//Convention: returns -1 if there is no such GffObj (i.e. last GffObj starts below x)
+//use qsearch to "position" a given coordinate x within a list of transcripts sorted 
+//by their start (lowest) coordinate; the returned int is the list index of the 
+//closest GffObj starting just *ABOVE* coordinate x
+//Convention: returns -1 if there is no such GffObj (i.e. last GffObj start <= x)
 int qsearch_mrnas(uint x, GList<GffObj>& mrnas);
-int qsearch_segs(uint x, GList<GSeg>& segs); // same as above, but for GSeg lists
+int qsearch_loci(uint x, GList<GLocus>& segs); // same as above, but for GSeg lists
 
 GSeqData* getRefData(int gid, GList<GSeqData>& ref_data); //returns reference GSeqData for a specific genomic sequence
 
diff --git a/src/hits.cpp b/src/hits.cpp
index 4b4c5c0..a3da696 100644
--- a/src/hits.cpp
+++ b/src/hits.cpp
@@ -46,6 +46,13 @@ bool hits_eq_mod_id(const ReadHit& lhs, const ReadHit& rhs)
 			lhs.cigar() == rhs.cigar());
 }
 
+bool hits_eq_non_multi(const MateHit& lhs, const MateHit& rhs)
+{
+	if ((lhs.is_multi() || rhs.is_multi()) && lhs.insert_id() != rhs.insert_id())
+		return false;
+	return hits_equals(lhs, rhs);
+}
+
 bool hits_equals(const MateHit& lhs, const MateHit& rhs) 
 {
 	if (lhs.ref_id() != rhs.ref_id())
@@ -74,13 +81,14 @@ bool has_no_collapse_mass(const MateHit& hit)
 }
 
 // Assumes hits are sorted by mate_hit_lt
+// Does not collapse hits that are multi-reads
 void collapse_hits(const vector<MateHit>& hits,
 				   vector<MateHit>& non_redundant)
 {
 	copy(hits.begin(), hits.end(), back_inserter(non_redundant));
 	vector<MateHit>::iterator new_end = unique(non_redundant.begin(), 
 											   non_redundant.end(), 
-											   hits_equals);
+											   hits_eq_non_multi);
 	non_redundant.erase(new_end, non_redundant.end());
     non_redundant.resize(non_redundant.size());
 	
@@ -91,18 +99,26 @@ void collapse_hits(const vector<MateHit>& hits,
 	size_t curr_unique_aln = 0;
 	while (curr_aln < hits.size())
 	{
-		if (hits_equals(non_redundant[curr_unique_aln], hits[curr_aln]) || hits_equals(non_redundant[++curr_unique_aln], hits[curr_aln]))
-			non_redundant[curr_unique_aln].incr_collapse_mass(hits[curr_aln].mass());
+		if (hits_eq_non_multi(non_redundant[curr_unique_aln], hits[curr_aln]) || hits_eq_non_multi(non_redundant[++curr_unique_aln], hits[curr_aln]))
+		{
+            double more_mass = hits[curr_aln].common_scale_mass();
+			//assert(non_redundant[curr_unique_aln].collapse_mass() == 0 || !non_redundant[curr_unique_aln].is_multi());
+			non_redundant[curr_unique_aln].incr_collapse_mass(more_mass);
+		}
 		else
 			assert(false);
 		
 		++curr_aln;
 	}
 	
-	non_redundant.erase(remove_if(non_redundant.begin(),non_redundant.end(),has_no_collapse_mass), non_redundant.end()); 
+	//foreach(MateHit& hit, non_redundant)
+		//assert(hit.collapse_mass() <= 1 || !hit.is_multi());
+	
+	//non_redundant.erase(remove_if(non_redundant.begin(),non_redundant.end(),has_no_collapse_mass), non_redundant.end()); 
 	
 }
 
+// Places multi-reads to the right of reads they match
 bool mate_hit_lt(const MateHit& lhs, const MateHit& rhs)
 {
 	if (lhs.left() != rhs.left())
@@ -151,6 +167,11 @@ bool mate_hit_lt(const MateHit& lhs, const MateHit& rhs)
 		}
 	}
 	
+	if (lhs.is_multi() != rhs.is_multi())
+	{
+		return rhs.is_multi();
+	}
+	
 	return false;
 }
 
@@ -164,11 +185,13 @@ ReadHit HitFactory::create_hit(const string& insert_name,
 							   const string& partner_ref,
 							   int partner_pos, 
 							   double error_prob,
-							   unsigned int edit_dist)
+							   unsigned int edit_dist,
+							   int num_hits,
+                               float base_mass)
 {
-	uint64_t insert_id = _insert_table.get_id(insert_name);
-	uint32_t reference_id = _ref_table.get_id(ref_name, NULL);
-	uint32_t partner_ref_id = _ref_table.get_id(partner_ref, NULL);
+	InsertID insert_id = _insert_table.get_id(insert_name);
+	RefID reference_id = _ref_table.get_id(ref_name, NULL);
+	RefID partner_ref_id = _ref_table.get_id(partner_ref, NULL);
 	
 	return ReadHit(reference_id,
 				   insert_id, 
@@ -179,7 +202,9 @@ ReadHit HitFactory::create_hit(const string& insert_name,
 				   partner_ref_id,
 				   partner_pos,
 				   error_prob,
-				   edit_dist);	
+				   edit_dist,
+				   num_hits,
+                   base_mass);	
 }
 
 ReadHit HitFactory::create_hit(const string& insert_name, 
@@ -191,11 +216,13 @@ ReadHit HitFactory::create_hit(const string& insert_name,
 							   const string& partner_ref,
 							   int partner_pos,
 							   double error_prob,
-							   unsigned int edit_dist)
+							   unsigned int edit_dist,
+							   int num_hits,
+                               float base_mass)
 {
-	uint64_t insert_id = _insert_table.get_id(insert_name);
-	uint32_t reference_id = _ref_table.get_id(ref_name, NULL);
-	uint32_t partner_ref_id = _ref_table.get_id(partner_ref, NULL);
+	InsertID insert_id = _insert_table.get_id(insert_name);
+	RefID reference_id = _ref_table.get_id(ref_name, NULL);
+	RefID partner_ref_id = _ref_table.get_id(partner_ref, NULL);
 	
 	return ReadHit(reference_id,
 				   insert_id, 
@@ -206,7 +233,9 @@ ReadHit HitFactory::create_hit(const string& insert_name,
 				   partner_ref_id,
 				   partner_pos,
 				   error_prob,
-				   edit_dist);	
+				   edit_dist,
+				   num_hits,
+                   base_mass);	
 }
 
 // populate a bam_t This will 
@@ -239,7 +268,7 @@ bool BAMHitFactory::next_record(const char*& buf, size_t& buf_size)
 
 CuffStrand use_stranded_protocol(uint32_t sam_flag, bool antisense_aln, MateStrandMapping msm)
 {
-	if (((sam_flag & BAM_FPAIRED) && (sam_flag & BAM_FREAD1)) || !(sam_flag & BAM_FPAIRED)) // left-most read or single-end
+	if (((sam_flag & BAM_FPAIRED) && (sam_flag & BAM_FREAD1)) || !(sam_flag & BAM_FPAIRED)) // first-in-pair or single-end
 	{
 		switch(msm)
 		{
@@ -253,7 +282,7 @@ CuffStrand use_stranded_protocol(uint32_t sam_flag, bool antisense_aln, MateStra
 				break;
 		}
 	}
-	else // right-most read
+	else // second-in-pair read
 	{
 		switch (msm)
 		{
@@ -289,6 +318,7 @@ bool BAMHitFactory::get_hit_from_buf(const char* orig_bwt_buf,
 	
 	vector<CigarOp> cigar;
 	bool spliced_alignment = false;
+	int num_hits = 1;
 	
 	double mapQ = hit_buf->core.qual;
 	long double error_prob;
@@ -316,7 +346,9 @@ bool BAMHitFactory::get_hit_from_buf(const char* orig_bwt_buf,
 						"*",
 						0,
 						1.0,
-						0);
+						0,
+						1,
+                        1.0);
 		return true;
 	}
 	
@@ -365,11 +397,11 @@ bool BAMHitFactory::get_hit_from_buf(const char* orig_bwt_buf,
 		if (mate_target_id == target_id)
 		{
 			mrnm = _hit_file->header->target_name[mate_target_id];
-			if (abs((int)text_mate_pos - (int)text_offset) > (int)max_intron_length)
-			{
-				//fprintf (stderr, "Mates are too distant, skipping\n");
-				return false;
-			}
+//			if (abs((int)text_mate_pos - (int)text_offset) > (int)max_intron_length)
+//			{
+//				//fprintf (stderr, "Mates are too distant, skipping\n");
+//				return false;
+//			}
 		}
 		else
 		{
@@ -401,6 +433,21 @@ bool BAMHitFactory::get_hit_from_buf(const char* orig_bwt_buf,
 		num_mismatches = bam_aux2i(ptr);
 	}
 
+	ptr = bam_aux_get(hit_buf, "NH");
+	if (ptr)
+	{
+		num_hits = bam_aux2i(ptr);
+	}
+    
+    double mass = 1.0;
+    ptr = bam_aux_get(hit_buf, "ZF");
+	if (ptr)
+	{
+		mass = bam_aux2i(ptr);
+        if (mass <= 0.0)
+            mass = 1.0;
+	}
+	
     bool antisense_aln = bam1_strand(hit_buf);
     
     if (_rg_props.strandedness() == STRANDED_PROTOCOL && source_strand == CUFF_STRAND_UNKNOWN)
@@ -408,7 +455,8 @@ bool BAMHitFactory::get_hit_from_buf(const char* orig_bwt_buf,
     
 	if (!spliced_alignment)
 	{
-		
+		//assert(_rg_props.strandedness() == STRANDED_PROTOCOL || source_strand == CUFF_STRAND_UNKNOWN);
+
 		//assert(cigar.size() == 1 && cigar[0].opcode == MATCH);
 		bh = create_hit(bam1_qname(hit_buf),
 						text_name,
@@ -419,7 +467,9 @@ bool BAMHitFactory::get_hit_from_buf(const char* orig_bwt_buf,
 						mrnm,
 						text_mate_pos,
 						error_prob,
-						num_mismatches);
+						num_mismatches,
+						num_hits,
+                        mass);
 		return true;
 		
 	}
@@ -439,7 +489,9 @@ bool BAMHitFactory::get_hit_from_buf(const char* orig_bwt_buf,
 						mrnm,
 						text_mate_pos,
 						error_prob,
-						num_mismatches);
+						num_mismatches,
+						num_hits,
+                        mass);
 		return true;
 	}
 	
@@ -512,13 +564,13 @@ bool HitFactory::parse_header_string(const string& header_rec,
                 // in an order consistent with the header, and to enforce that
                 // BAM records appear in the order implied by the header
                 RefID _id = _ref_table.get_id(fields[1], NULL);
-                
-                const RefSequenceTable::SequenceInfo* info = _ref_table.get_info(_id);
 
+                const RefSequenceTable::SequenceInfo* info = _ref_table.get_info(_id);
+				
                 if (info->observation_order != _num_seq_header_recs)
                 {
                     fprintf(stderr, "Error: sort order of reads in BAMs must be the same\n");
-                    exit(1);
+					exit(1);
                 }
             }
         }
@@ -543,7 +595,7 @@ void HitFactory::finalize_rg_props()
     }
 }
 
-static const int MAX_HEADER_LEN = 4 * 1024 * 1024; // 4 MB
+static const unsigned MAX_HEADER_LEN = 4 * 1024 * 1024; // 4 MB
 
 bool BAMHitFactory::inspect_header()
 {
@@ -616,7 +668,8 @@ bool SAMHitFactory::get_hit_from_buf(const char* orig_bwt_buf,
 									 char* name_out,
 									 char* name_tags)
 {	
-	char bwt_buf[2048];
+	char bwt_buf[10*2048];
+
 	strcpy(bwt_buf, orig_bwt_buf);
 	// Are we still in the header region?
 	if (bwt_buf[0] == '@')
@@ -691,6 +744,7 @@ bool SAMHitFactory::get_hit_from_buf(const char* orig_bwt_buf,
 	//int len = strlen(sequence);
 	vector<CigarOp> cigar;
 	bool spliced_alignment = false;
+	int num_hits = 1;
 	
 	double mapQ = atoi(map_qual_str);
 	long double error_prob;
@@ -716,7 +770,9 @@ bool SAMHitFactory::get_hit_from_buf(const char* orig_bwt_buf,
 						"*",
 						0,
 						1.0,
-						0);
+						0,
+						1,
+                        1.0);
 		return true;
 	}
 	// Mostly pilfered direct from the SAM tools:
@@ -780,11 +836,11 @@ bool SAMHitFactory::get_hit_from_buf(const char* orig_bwt_buf,
 		if (!strcmp(mate_ref_name, "=") || !strcmp(mate_ref_name, text_name))
 		{
 			mrnm = text_name;
-			if (abs((int)text_mate_pos - (int)text_offset) > (int)max_intron_length)
-			{
-				//fprintf (stderr, "Mates are too distant, skipping\n");
-				return false;
-			}
+//			if (abs((int)text_mate_pos - (int)text_offset) > (int)max_intron_length)
+//			{
+//				//fprintf (stderr, "Mates are too distant, skipping\n");
+//				return false;
+//			}
 		}
 		else
 		{
@@ -802,6 +858,8 @@ bool SAMHitFactory::get_hit_from_buf(const char* orig_bwt_buf,
 	
 	const char* tag_buf = buf;
 	
+    double mass = 1.0;
+    
 	while((tag_buf = strsep((char**)&buf,"\t")))
 	{
 		
@@ -829,6 +887,16 @@ bool SAMHitFactory::get_hit_from_buf(const char* orig_bwt_buf,
 				{
 					num_mismatches = atoi(third_token);
 				}
+				else if (!strcmp(first_token, "NH"))
+				{
+                    num_hits = atoi(third_token);
+				}
+                else if (!strcmp(first_token, "ZF"))
+				{
+					mass = atof(third_token);
+                    if (mass <= 0.0)
+                        mass = 1.0;
+				}
 				else 
 				{
 					
@@ -844,8 +912,7 @@ bool SAMHitFactory::get_hit_from_buf(const char* orig_bwt_buf,
 		source_strand = use_stranded_protocol(sam_flag, antisense_aln, _rg_props.mate_strand_mapping());
 	
 	if (!spliced_alignment)
-	{
-		
+	{		
 		//assert(cigar.size() == 1 && cigar[0].opcode == MATCH);
 		bh = create_hit(name,
 						text_name,
@@ -856,7 +923,9 @@ bool SAMHitFactory::get_hit_from_buf(const char* orig_bwt_buf,
 						mrnm,
 						text_mate_pos - 1,
 						error_prob,
-						num_mismatches);
+						num_mismatches,
+						num_hits,
+                        mass);
 		return true;
 		
 	}
@@ -876,7 +945,9 @@ bool SAMHitFactory::get_hit_from_buf(const char* orig_bwt_buf,
 						mrnm,
 						text_mate_pos - 1,
 						error_prob,
-						num_mismatches);
+						num_mismatches,
+						num_hits,
+                        mass);
 		return true;
 	}
 	return false;
diff --git a/src/hits.h b/src/hits.h
index b1bee61..38779b2 100644
--- a/src/hits.h
+++ b/src/hits.h
@@ -16,9 +16,10 @@
 
 #include <boost/shared_ptr.hpp>
 
-#include <bam/sam.h>
+#include <samtools/sam.h>
 
 #include "common.h"
+#include "multireads.h"
 
 using namespace std;
 using boost::shared_ptr;
@@ -57,12 +58,12 @@ struct CigarOp
 };
 
 typedef uint64_t InsertID;
-typedef uint32_t RefID;
+typedef uint64_t RefID;
 
 extern int num_deleted;
 
 /*  Stores the information from a single record of the bowtie map. A given read
-    may have many of these.  Reads up to 255bp are supported. 
+    may have many of these.
 */
 struct ReadHit
 {
@@ -70,7 +71,9 @@ struct ReadHit
 		_ref_id(0),
 		_insert_id(0),
 		_error_prob(1.0),
-		_edit_dist(0xFFFFFFFF)
+        _base_mass(1.0),
+        _edit_dist(0xFFFFFFFF),
+		_num_hits(1)
     {
         num_deleted++;
     }
@@ -84,7 +87,9 @@ struct ReadHit
 			RefID partner_ref,
 			int partner_pos,
 			double error_prob,
-			unsigned int edit_dist) :
+			unsigned int edit_dist,
+			int num_hits,
+            float base_mass) :
 		_ref_id(ref_id),
 		_insert_id(insert_id), 
 		_left(left), 
@@ -94,7 +99,9 @@ struct ReadHit
 		_source_strand(source_strand),
 		_antisense_aln(antisense),
 		_error_prob(error_prob),
-		_edit_dist(edit_dist)
+        _base_mass(base_mass),
+        _edit_dist(edit_dist),
+		_num_hits(num_hits)
 	{
 		assert(_cigar.capacity() == _cigar.size());
 		_right = get_right();
@@ -110,7 +117,9 @@ struct ReadHit
 			RefID partner_ref,
 			int partner_pos, 
 			double error_prob,
-			unsigned int  edit_dist) : 
+			unsigned int  edit_dist,
+			int num_hits,
+            float base_mass) : 
 		_ref_id(ref_id),
 		_insert_id(insert_id), 	
 		_left(left),
@@ -120,7 +129,9 @@ struct ReadHit
 		_source_strand(source_strand),
 		_antisense_aln(antisense_aln),
 		_error_prob(error_prob),
-		_edit_dist(edit_dist)
+        _base_mass(base_mass),
+        _edit_dist(edit_dist),
+		_num_hits(num_hits)
 	{
 		assert(_cigar.capacity() == _cigar.size());
 		_right = get_right();
@@ -138,7 +149,9 @@ struct ReadHit
 		_source_strand = other._source_strand;
 		_antisense_aln = other._antisense_aln;
 		_error_prob = other._error_prob;
-		_edit_dist = other._edit_dist;
+		_num_hits = other._num_hits;
+        _base_mass = other._base_mass;
+        _edit_dist = other._edit_dist;
         _right = get_right();
         num_deleted++;
     }
@@ -171,8 +184,19 @@ struct ReadHit
 	
 	bool contains_splice() const
 	{
-                // FIXME: This won't be true once we start supporting INDEL operators in CIGAR strings. See Ticket #183
-		return (_cigar.size() > 1);
+		for (size_t i = 0; i < _cigar.size(); ++i)
+		{
+				if (_cigar[i].opcode == REF_SKIP)
+					return true;
+		}
+		return false;
+	}
+	
+	bool is_singleton() const
+	{ 
+		return (partner_ref_id() == 0 ||
+				partner_ref_id() != ref_id() ||
+				abs(partner_pos() - left()) > max_partner_dist);
 	}
 	
 	bool operator==(const ReadHit& rhs) const
@@ -196,9 +220,20 @@ struct ReadHit
 	int right() const					{ return _right;			}
 	CuffStrand source_strand()	const	{ return _source_strand; }
 	bool antisense_align() const		{ return _antisense_aln;	}
-		
+	
 	double error_prob() const			{ return _error_prob;		}
 	
+	// Number of multi-hits for this read
+	int num_hits() const				{ return _num_hits;			}
+	
+    // We are ignoring the _base_mass and re-calculating based on multi-hits
+	double mass() const 
+	{
+		if (is_singleton())
+			return 1.0/_num_hits;
+		return 0.5 / _num_hits;
+	}
+	
 	// For convenience, if you just want a copy of the gap intervals
 	// for this hit.
 	void gaps(vector<pair<int,int> >& gaps_out) const
@@ -242,10 +277,10 @@ struct ReadHit
 		return _cigar.size() == 1 && _cigar[0].opcode == MATCH;
 	}
 	
-	unsigned int  edit_dist() const { return _edit_dist; }
-	
-	const string& hitfile_rec() const { return _hitfile_rec; }
-	void hitfile_rec(const string& rec) { _hitfile_rec = rec; }
+    unsigned int  edit_dist() const { return _edit_dist; }
+    
+	//const string& hitfile_rec() const { return _hitfile_rec; }
+	//void hitfile_rec(const string& rec) { _hitfile_rec = rec; }
 	
 private:
 	
@@ -286,9 +321,11 @@ private:
 	
 	CuffStrand _source_strand;    // Which strand the read really came from, if known
 	bool _antisense_aln;       // Whether the alignment is to the reverse strand
-	double _error_prob;		   // Probability that this alignment is incorrect
-	unsigned int  _edit_dist;            // Number of mismatches
-	string _hitfile_rec; // Points to the buffer for the record from which this hit came
+	float _error_prob;		   // Probability that this alignment is incorrect
+    float _base_mass;
+    unsigned int  _edit_dist;            // Number of mismatches
+	int _num_hits; // Number of multi-hits (1 by default)
+	//string _hitfile_rec; // Points to the buffer for the record from which this hit came
 };
 
 class ReadTable
@@ -366,7 +403,7 @@ public:
 #if ENABLE_THREADS
         table_lock.lock();
 #endif
-		uint32_t _id = hash_string(name.c_str());
+		uint64_t _id = hash_string(name.c_str());
 		pair<InvertedIDTable::iterator, bool> ret = 
 		_by_id.insert(make_pair(_id, SequenceInfo(_next_id, NULL, NULL)));
 		if (ret.second == true)
@@ -475,16 +512,26 @@ public:
 private:
 	
 	// This is FNV-1, see http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash
-	inline uint32_t hash_string(const char* __s)
+	inline uint64_t hash_string(const char* __s)
 	{
-		uint32_t hash = 0x811c9dc5;
+		uint64_t hash = 0xcbf29ce484222325ull;
 		for ( ; *__s; ++__s)
 		{
-			hash *= 16777619;
+			hash *= 1099511628211ull;
 			hash ^= *__s;
 		}
 		return hash;
 	}
+//	inline uint32_t hash_string(const char* __s)
+//	{
+//		uint32_t hash = 0x811c9dc5;
+//		for ( ; *__s; ++__s)
+//		{
+//			hash *= 16777619;
+//			hash ^= *__s;
+//		}
+//		return hash;
+//	}
 	
 	//IDTable _by_name;
 	RefID _next_id;
@@ -534,7 +581,9 @@ public:
 					   const string& partner_ref,
 					   int partner_pos,
 					   double error_prob,
-					   unsigned int  edit_dist);
+					   unsigned int  edit_dist,
+					   int num_hits,
+                       float base_mass);
 	
 	ReadHit create_hit(const string& insert_name, 
 					   const string& ref_name,
@@ -545,7 +594,9 @@ public:
 					   const string& partner_ref,
 					   int partner_pos,
 					   double error_prob,
-					   unsigned int  edit_dist);
+					   unsigned int  edit_dist,
+					   int num_hits,
+                       float base_mass);
 	
 	virtual void reset() = 0;
 	
@@ -589,7 +640,8 @@ private:
     
 	ReadTable& _insert_table;
 	RefSequenceTable& _ref_table;
-    size_t _num_seq_header_recs;
+    uint32_t _num_seq_header_recs;
+
 
 };
 
@@ -775,11 +827,10 @@ public:
     _left_alignment(NULL),
     _right_alignment(NULL),
     _collapse_mass(0.0),
-    _is_mapped(false),
-    _collapsed_to(NULL){}
+    _is_mapped(false){}
     
 	MateHit(shared_ptr<ReadGroupProperties const> rg_props,
-            uint32_t refid, 
+            RefID refid, 
 			const ReadHit* left_alignment, 
 			const ReadHit* right_alignment) : 
     _rg_props(rg_props),
@@ -787,8 +838,7 @@ public:
 	_left_alignment(left_alignment),
 	_right_alignment(right_alignment),
 	_collapse_mass(0.0),
-	_is_mapped(false),
-	_collapsed_to(NULL)
+	_is_mapped(false)
 	{
 		//_expected_inner_dist = min(genomic_inner_dist(), _expected_inner_dist);
 	}
@@ -817,18 +867,23 @@ public:
 	void is_mapped(bool mapped) 
 	{ 
 		_is_mapped = mapped; 
-		if (_collapsed_to)
-			_collapsed_to->is_mapped(mapped);
 	}
 	
-	MateHit* collapsed_to() const { return _collapsed_to; }
-	void collapsed_to(MateHit* hit) { _collapsed_to = hit; }
+	int num_hits() const 
+	{
+		assert(_left_alignment);
+		return _left_alignment->num_hits();
+	}
 	
+	bool is_multi() const 
+	{
+		return num_hits() > 1;
+	}
+		
 	bool is_pair() const
 	{
 		return (_left_alignment && _right_alignment);
 	}
-	
 
 	int left() const 
 	{
@@ -877,6 +932,13 @@ public:
 	}
 	
 	
+	bool contains_splice() const
+	{
+		if (_right_alignment)
+			return (_left_alignment->contains_splice() || _right_alignment->contains_splice());
+		return (_left_alignment->contains_splice());
+	}
+	
 	InsertID insert_id() const
 	{
 		if (_left_alignment) return _left_alignment->insert_id();
@@ -921,13 +983,29 @@ public:
 		return make_pair(-1,-1);
 	}
 	
-	double error_prob() const
+	// MRT is incorrect and not added to rg_props until after inspect_map
+    // We are ignoring the mass reported by the ReadHits and re-calculating based on multi-hits
+	double mass() const
 	{
-		if (_left_alignment)
-			return _left_alignment->error_prob();
-		else if (_right_alignment)
-			return _right_alignment->error_prob();
-		return 1.0;
+        double base_mass = 1.0;
+
+        if (is_multi())
+		{
+			shared_ptr<MultiReadTable> mrt = _rg_props->multi_read_table();
+			if (mrt)
+				return mrt->get_mass(*this);
+			else
+				return base_mass/num_hits();
+		}
+		return base_mass;
+	}
+    
+	double common_scale_mass() const
+	{
+       	double m = mass();
+        m *= _rg_props->mass_scale_factor();
+        
+        return m;
 	}
 	
 	unsigned int  edit_dist() const
@@ -940,7 +1018,6 @@ public:
 		return edits;
 	}
 	
-	double mass() const { return (1.0 - error_prob()); }
 	double collapse_mass() const { return _collapse_mass; }
 	void collapse_mass(double m) { _collapse_mass = m; }
 	void incr_collapse_mass(double incr) { _collapse_mass += incr; }
@@ -953,7 +1030,6 @@ private:
 	const ReadHit* _right_alignment;
 	double _collapse_mass;
 	bool _is_mapped;
-	MateHit* _collapsed_to;
 	//bool _closed;
 };
 
@@ -961,6 +1037,8 @@ bool mate_hit_lt(const MateHit& lhs, const MateHit& rhs);
 
 bool hits_eq_mod_id(const ReadHit& lhs, const ReadHit& rhs);
 
+bool hits_eq_non_multi(const MateHit& lhs, const MateHit& rhs);
+
 bool hits_equals(const MateHit& lhs, const MateHit& rhs);
 
 bool has_no_collapse_mass(const MateHit& hit);
diff --git a/src/jensen_shannon.cpp b/src/jensen_shannon.cpp
index 088d622..6c61395 100644
--- a/src/jensen_shannon.cpp
+++ b/src/jensen_shannon.cpp
@@ -40,7 +40,7 @@ double jensen_shannon_div(vector<ublas::vector<double> >& sample_kappas)
 									  sample_kappas[i].end(), 0.0);
 		if (abs(kappa_sum - 1.0) > 1e-10)
 		{
-			cerr << kappa_sum << " " << sample_kappas[i] << endl;
+			//cerr << kappa_sum << " " << sample_kappas[i] << endl;
 		}
 		assert (abs(kappa_sum - 1.0) < 1e-10);
 	}
@@ -72,6 +72,7 @@ double jensen_shannon_div(vector<ublas::vector<double> >& sample_kappas)
 	double entropy_avg = entropy(avg_kappas);
 	
 	double js = entropy_avg - avg_entropy;
+
 	return sqrt(js);
 }
 
@@ -171,4 +172,4 @@ void make_js_covariance_matrix(vector<ublas::matrix<double> >& kappa_covariances
 			}
 		}
 	}
-}
\ No newline at end of file
+}
diff --git a/src/jensen_shannon.h b/src/jensen_shannon.h
index d198820..5e635e2 100644
--- a/src/jensen_shannon.h
+++ b/src/jensen_shannon.h
@@ -26,4 +26,4 @@ void jensen_shannon_gradient(std::vector<ublas::vector<double> >& sample_kappas,
 							 ublas::vector<double>& gradient);
 
 void make_js_covariance_matrix(std::vector<ublas::matrix<double> >& kappa_covariances,
-							   ublas::matrix<double>& js_covariance);
\ No newline at end of file
+							   ublas::matrix<double>& js_covariance);
diff --git a/src/locfit/adap.c b/src/locfit/adap.c
new file mode 100644
index 0000000..eb1b48c
--- /dev/null
+++ b/src/locfit/adap.c
@@ -0,0 +1,195 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ */
+
+/*
+  Functions implementing the adaptive bandwidth selection.
+  Will make the final call to nbhd() to set smoothing weights
+  for selected bandwidth, But will **not** make the
+  final call to locfit().
+*/
+
+#include "local.h"
+
+static double hmin;
+
+double acri(lk,t0,t2,pen)
+double lk, t0, t2, pen;
+{ double y;
+/* return(-2*lk/(t0*exp(pen*log(1-t2/t0)))); */
+  /* return((-2*lk+pen*t2)/t0); */
+  y = (MAX(-2*lk,t0-t2)+pen*t2)/t0;
+  return(y);
+}
+
+double mmse(lf,des)
+lfit *lf;
+design *des;
+{ int i, ii, j, p, p1;
+  double sv, sb, *l, dp;
+  l = des->wd;
+  wdiag(lf,des,l,(INT)0,(INT)1,(INT)0);
+  sv = sb = 0;
+  p = lf->mi[MP];
+  for (i=0; i<des->n; i++)
+  { sv += l[i]*l[i];
+    ii = des->ind[i];
+    dp = des->di[ii];
+    for (j=0; j<lf->mi[MDEG]; j++) dp *= des->di[ii];
+    sb += fabs(l[i])*dp;
+  }
+  p1 = factorial((int)lf->mi[MDEG]+1);
+  return(sv+sb*sb*lf->dp[DADP]*lf->dp[DADP]/(p1*p1));
+}
+
+static double mcp, clo, cup;
+
+/*
+  Initial bandwidth will be (by default)
+  k-nearest neighbors for k small, just lage enough to
+  get defined estimate (unless user provided nonzero DALP
+  or DFXH components)
+*/
+
+INT ainitband(des,lf)
+design *des;
+lfit   *lf;
+{ INT lf_status = LF_OK, p, z, cri, noit, redo;
+  double h, ho, t[6];
+  p = des->p;
+  cri = lf->mi[MACRI];
+  noit = !((cri==AOK) | (cri==ANONE));
+  z = (INT)(lf->mi[MN]*lf->dp[DALP]);
+  if ((noit) && (z<p+2)) z = p+2;
+  redo = 0; ho = -1;
+  do
+  { h = nbhd(lf,des,z,lf->dp[DFXH],redo);
+    if (z<des->n) z = des->n;
+    if (h>ho) lf_status = locfit(lf,des,h,noit);
+    if (cri==ANONE) return(lf_status);
+    z++;
+    redo = 1;
+  } while ((z<=lf->mi[MN]) && ((h==0)||(lf_status!=LF_OK)));
+  hmin = h;
+
+  switch(lf->mi[MACRI])
+  { case ACP:
+      local_df(lf,des,t);
+      mcp = acri(des->llk,t[0],t[2],lf->dp[DADP]);
+      return(lf_status);
+    case AKAT:
+      local_df(lf,des,t);
+      clo = des->cf[0]-lf->dp[DADP]*t[5];
+      cup = des->cf[0]+lf->dp[DADP]*t[5];
+      return(lf_status);
+    case AMDI:
+      mcp = mmse(lf,des);
+      return(lf_status);
+    case AOK: return(lf_status);
+  }
+  ERROR(("aband1: unknown criterion"));
+  return(LF_ERR);
+}
+
+/*
+  aband2 increases the initial bandwidth until lack of fit results,
+  or the fit is close to a global fit. Increase h by 1+0.3/d at
+  each iteration.
+*/
+
+double aband2(des,lf,h0)
+design *des;
+lfit   *lf;
+double h0;
+{ double t[6], h, h1, nu1, cp, ncp, tlo, tup;
+  INT d, inc, n, p, done;
+  d = lf->mi[MDIM]; n = lf->mi[MN]; p = lf->mi[MP];
+  h1 = h = h0;
+  done = 0; nu1 = 0.0;
+  inc = 0; ncp = 0.0;
+  while ((!done) & (nu1<(n-p)*0.95))
+  { h = nbhd(lf,des,0,(1+0.3/d)*h,1);
+    if (locfit(lf,des,h,1)>0) WARN(("aband2: failed fit"));
+    local_df(lf,des,t);
+    nu1 = t[0]-t[2]; /* tr(A) */
+    switch(lf->mi[MACRI])
+    { case AKAT:
+        tlo = des->cf[0]-lf->dp[DADP]*t[5];
+        tup = des->cf[0]+lf->dp[DADP]*t[5];
+/* printf("h %8.5f  tlo %8.5f  tup %8.5f\n",h,tlo,tup); */
+        done = ((tlo>cup) | (tup<clo));
+        if (!done)
+        { clo = MAX(clo,tlo);
+          cup = MIN(cup,tup);
+          h1 = h;
+        }
+        break;
+      case ACP:
+        cp = acri(des->llk,t[0],t[2],lf->dp[DADP]);
+/* printf("h %8.5f  lk %8.5f  t0 %8.5f  t2 %8.5f  cp %8.5f\n",h,des->llk,t[0],t[2],cp); */
+        if (cp<mcp) { mcp = cp; h1 = h; }
+        if (cp>=ncp) inc++; else inc = 0;
+        ncp = cp;
+        done = (inc>=10) | ((inc>=3) & ((t[0]-t[2])>=10) & (cp>1.5*mcp));
+        break;
+      case AMDI:
+        cp = mmse(lf,des);
+        if (cp<mcp) { mcp = cp; h1 = h; }
+        if (cp>ncp) inc++; else inc = 0;
+        ncp = cp;
+        done = (inc>=3);
+        break;
+    }
+  }
+  return(h1);
+}
+
+/*
+  aband3 does a finer search around best h so far. Try
+  h*(1-0.2/d), h/(1-0.1/d), h*(1+0.1/d), h*(1+0.2/d)
+*/
+double aband3(des,lf,h0)
+design *des;
+lfit   *lf;
+double h0;
+{ double t[6], h, h1, cp, tlo, tup;
+  INT i, i0, d, n;
+  d = lf->mi[MDIM]; n = lf->mi[MN];
+
+  h1 = h0;
+  i0 = (lf->mi[MACRI]==AKAT) ? 1 : -2;
+  if (h0==hmin) i0 = 1;
+  for (i=i0; i<=2; i++)
+  { if (i==0) i++;
+    h = h0*(1+0.1*i/lf->mi[MDIM]);
+    h = nbhd(lf,des,0,h,1);
+    if (locfit(lf,des,h,1)>0) WARN(("aband3: failed fit"));
+    local_df(lf,des,t);
+    switch (lf->mi[MACRI])
+    { case AKAT:
+        tlo = des->cf[0]-lf->dp[DADP]*t[5];
+        tup = des->cf[0]+lf->dp[DADP]*t[5];
+        if ((tlo>cup) | (tup<clo)) /* done */
+          i = 2;
+        else
+        { h1 = h;
+          clo = MAX(clo,tlo);
+          cup = MIN(cup,tup);
+        }
+        break;
+      case ACP:
+        cp = acri(des->llk,t[0],t[2],lf->dp[DADP]);
+        if (cp<mcp) { mcp = cp; h1 = h; }
+        else
+        { if (i>0) i = 2; }
+        break;
+      case AMDI:
+        cp = mmse(lf,des);
+        if (cp<mcp) { mcp = cp; h1 = h; }
+        else
+        { if (i>0) i = 2; }
+    }
+  }
+  return(h1);
+}
diff --git a/src/locfit/ar_funs.c b/src/locfit/ar_funs.c
new file mode 100644
index 0000000..55889e5
--- /dev/null
+++ b/src/locfit/ar_funs.c
@@ -0,0 +1,105 @@
+/*
+ *   Copyright (c) 1996-2000 Lucent Technologies.
+ *   See README file for details.
+ * 
+ *
+ *  ar_setfunction sets a function pointer on the arithmetic structure,
+ *    according to the first len compnents of the string z.
+ *    Also sets the rt->cmd field, and returns the required number of
+ *    arguments.
+ */
+
+#include "local.h"
+
+double vrexp()  { return(rexp(1.0)); }
+double vrnorm() { return(rnorm(0.0,1.0)); }
+double vpnorm(double x){ return(pnorm(x,0.0,1.0)); }
+double vdnorm(double x){ return(exp(-x*x/2)/S2PI); }
+double dummyf() { return(0.0); }
+double frac(double x) { return(x-floor(x)); }
+
+double vmin(v)
+vari *v;
+{ int i;
+  double z, x;
+  z = vitem(v,0);
+  for (i=1; i<vlength(v); i++)
+  { x = vitem(v,i);
+    if (x<z) z = x;
+  }
+  return(z);
+}
+
+double vmax(v)
+vari *v;
+{ int i;
+  double z, x;
+  z = vitem(v,0);
+  for (i=1; i<vlength(v); i++)
+  { x = vitem(v,i);
+    if (x>z) z = x;
+  }
+  return(z);
+}
+
+double vsum(v)
+vari *v;
+{ int i;
+  double z;
+  z = 0.0;
+  for (i=0; i<vlength(v); i++) z += vitem(v,i);
+  return(z);
+}
+double vmean(vari *v) { return(vsum(v)/vlength(v)); }
+
+int ar_setfunction(rt,z,len)
+arstruct *rt;
+int z;
+int len;
+{ int rargs;
+  rt->f = NULL;
+  rt->cmd = 'f';
+  rargs = 1;
+  if (len==3)
+  { if (stm(z,"sin",3)) rt->f = sin;
+    if (stm(z,"cos",3)) rt->f = cos;
+    if (stm(z,"tan",3)) rt->f = tan;
+    if (stm(z,"exp",3)) rt->f = exp;
+    if (stm(z,"log",3)) rt->f = log;
+    if (stm(z,"abs",3)) rt->f = fabs;
+    if (stm(z,"seq",3)) { rt->f = dummyf; rt->cmd = 'Q'; rargs=3; }
+    if (stm(z,"min",3)) { rt->f = vmin; rt->cmd = 'S'; rargs=1; }
+    if (stm(z,"max",3)) { rt->f = vmax; rt->cmd = 'S'; rargs=1; }
+    if (stm(z,"sum",3)) { rt->f = vsum; rt->cmd = 'S'; rargs=1; }
+    if (stm(z,"rep",3)) { rt->f = dummyf; rt->cmd = 'R'; rargs=2; }
+  }
+  if (len==4)
+  { if (stm(z,"frac",4)) rt->f = frac;
+    if (stm(z,"sqrt",4)) rt->f = sqrt;
+    if (stm(z,"rexp",4)) { rt->f = vrexp; rt->cmd = 'G'; }
+    if (stm(z,"mean",4)) { rt->f = vmean; rt->cmd = 'S'; rargs=1; }
+  }
+  if (len==5)
+  { if (stm(z,"floor",5)) rt->f = floor;
+    if (stm(z,"pnorm",5)) rt->f = vpnorm;
+    if (stm(z,"dnorm",5)) rt->f = vdnorm;
+    if (stm(z,"logit",5)) rt->f = logit;
+    if (stm(z,"expit",5)) rt->f = expit;
+    if (stm(z,"runif",5)) { rt->f = runif; rt->cmd='G'; }
+    if (stm(z,"rnorm",5)) { rt->f = vrnorm;rt->cmd='G'; }
+    if (stm(z,"rpois",5)) { rt->f = rpois; rt->cmd='H'; rargs=2; }
+  }
+  if (len==6)
+  { if (stm(z,"sample",6)) { rt->f = dummyf;rt->cmd='M'; rargs=2; }
+    if (stm(z,"fitted",6)) { rt->f = dummyf; rt->cmd='Z'; rargs=0; }
+  }
+  if (len==9)
+  { if (stm(z,"residuals",9))
+    { rt->f= dummyf; rt->cmd='Y'; rargs=0; }
+  }
+  if (rt->f==NULL)
+  { rt->cmd = 'e';
+    ERROR(("unknown function"));
+  }
+  return(rargs);
+}
diff --git a/src/locfit/arith.c b/src/locfit/arith.c
new file mode 100644
index 0000000..536a78c
--- /dev/null
+++ b/src/locfit/arith.c
@@ -0,0 +1,619 @@
+/*
+ *   Copyright (c) 1996-2000 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include <ctype.h>
+#include "local.h"
+
+#ifdef CVERSION
+
+extern lfit lf;
+extern int ar_setfunction();
+
+double vadd(double e1,double e2) { return(e1+e2); }
+double vsub(double e1,double e2) { return(e1-e2); }
+double vmul(double e1,double e2) { return(e1*e2); }
+double vdiv(double e1,double e2) { return(e1/e2); }
+double vpow(double e1,double e2)
+{ if (e2==2) return(e1*e1);
+    if (e1<=0) return(0.0);
+    return(exp(e2*log(e1)));
+}
+double vgt(double e1,double e2) { return((double)(e1>e2)); }
+double vlt(double e1,double e2) { return((double)(e1<e2)); }
+double vge(double e1,double e2) { return((double)(e1>=e2)); }
+double vle(double e1,double e2) { return((double)(e1<=e2)); }
+double veq(double e1,double e2) { return((double)(e1==e2)); }
+double vne(double e1,double e2) { return((double)(e1!=e2)); }
+
+arstruct art;
+
+double lf_exp(double x) { return (x<700.0) ? exp(x) : exp(700.0); }
+
+double vseq(double a,double b,int i,int n) { return(a+(b-a)*i/(n-1)); }
+
+double rsample(v)
+vari *v;
+{ int i;
+    i = (int)( runif() * vlength(v) );
+    return( vitem(v,i) );
+}
+
+vari *vrep(v1,v2)
+vari *v1, *v2;
+{ int i, j, k, m, n;
+    vari *v;
+    n = 0;
+    for (i=0; i<vlength(v1); i++) n += vitem(v2,i);
+    v = createvar("_vrep",STHIDDEN,n,VDOUBLE);
+    k = 0;
+    if (vlength(v2)==1)
+    { m = vitem(v2,0);
+        for (i=0; i<m; i++)
+            for (j=0; j<vlength(v1); j++)
+                vassn(v,k++,vitem(v1,j));
+    }
+    else
+    { for (i=0; i<vlength(v1); i++)
+    { m = vitem(v2,i);
+        for (j=0; j<m; j++) vassn(v,k++,vitem(v1,i));
+    }
+    }
+    deleteifhidden(v1);
+    deleteifhidden(v2);
+    return(v);
+}
+
+/*
+ Set the arguments on arith structures.
+ Argument number p (0,1,2; max args = 3)
+ n is the index of source arstruct.
+ k is the index of the argument arstruct.
+ */
+void setnext(va,n,p,k)
+vari *va;
+int n, p, k;
+{ arstruct *rt;
+    rt = viptr(va,n);
+    if (p>=3)
+    { ERROR(("Too many function arguments"));
+        return;
+    }
+    rt->nx[p] = k;
+}
+
+void prars(v,i)
+vari *v;
+int i;
+{ arstruct *ars;
+    ars = (arstruct *)viptr(v,i);
+    printf("%d %c\n",i,ars->cmd);
+}
+
+arstruct *cmdptr(v,i)
+vari *v;
+int i;
+{ return((arstruct *)viptr(v,i));
+}
+
+int isstring(z,i1,i2)
+char *z;
+int i1, i2;
+{ int i;
+    if ((z[i1] != '"') | (z[i2] != '"')) return(0);
+    for (i=i1+1; i<i2; i++) if (z[i]=='"') return(0);
+    return(1);
+}
+
+int isname(z,i1,i2)
+char *z;
+int i1, i2;
+{ int i;
+    if (!isalpha(z[i1])) return(0);
+    for (i=i1+1; i<=i2; i++)
+        if (!isalnum(z[i])) return(0);
+    return(1);
+}
+
+int isfunction(z,i1,i2)
+char *z;
+int i1, i2;
+{ int i;
+    if (z[i2] != ')') return(0);
+    i = matchlf(z,i1,i2,'(',')');
+    if (lf_error) return(0);
+    return(isname(z,i1,i-1));
+}
+
+int issubset(z,i1,i2)
+char *z;
+int i1, i2;
+{ int i;
+    if (z[i2] != ']') return(0);
+    i = matchlf(z,i1,i2,'[',']');
+    if (lf_error) return(0);
+    return(isname(z,i1,i-1));
+}
+
+int isNumber(z,i1,i2,val)
+char *z;
+int i1, i2;
+double *val;
+{ char *end;
+    *val = strtod(&z[i1],&end);
+    return(end==&z[i2+1]);
+}
+
+int isoperator(z,i1,i2)
+char *z;
+int i1, i2;
+{ int i;
+    
+    i = checkrtol(z,i1,i2,",");
+    if (i >= i1) return(i);
+    
+    i = checkrtol(z,i1,i2,"=<>");
+    if (i > i1) return(i);
+    
+    i = checkrtol(z,i1,i2,"+-");
+    while ((i>i1) && (strchr("+-*/:^",z[i-1])!=NULL))
+        i = checkrtol(z,i1+1,i-1,"+-");
+    if (i>i1) return(i);
+    
+    i = checkrtol(z,i1,i2,"*/");
+    if (i >= i1) return(i);
+    
+    /* looks like a weird priority for : (sequence) but seems to match S. */
+    i = checkrtol(z,i1,i2,":");
+    if (i >= i1) return(i);
+    
+    i = checkrtol(z,i1,i2,"^");
+    if (i >= i1) return(i);
+    
+    return(-1);
+}
+
+vari *arbuild(z,i1,i2,va,k,dum) /* k=0/1 search variable names? */
+char *z;                        /* dum: are dummies x0, x1 etc allowed? */
+INT i1, i2, k, dum;
+vari *va;
+{ INT al, ar, i, j, n, nargs, rargs, inum;
+    vari *v;
+    arstruct *rt;
+    char tmp;
+    double val;
+    
+    if (va==NULL)
+    { va = createvar("_varith",STSYSTEM,10,VARC);
+        if (lf_error || va == NULL) 
+        {
+            return(NULL);   
+        }
+        else
+        {
+            vlength(va) = 0;
+        }
+    }
+    
+    n = vlength(va);
+    if (vbytes(n+1,VARC)>va->bytes) /* need to grow */
+    { v = va;
+        setvarname(va,"_ovarith");
+        va = createvar("_varith",STSYSTEM,n+5,VARC);
+        vlength(va) = n;
+        memcpy(vdptr(va),vdptr(v),vbytes(n,VARC));
+        deletevar(v);
+    }
+    inum = n;
+    vlength(va) = n+1;
+    
+    while ((z[i1]=='(') && (matchrt(z,i1,i2,'(',')')==i2)) { i1++; i2--; }
+    
+    if (isNumber(z,i1,i2,&val))
+    { rt = cmdptr(va,inum);
+        rt->cmd = 'D';
+        rt->x = val;
+        return(va);
+    }
+    
+    if (isstring(z,i1,i2))
+    { rt = cmdptr(va,inum);
+        rt->cmd = 's';
+        rt->vv = createvar("_string",STHIDDEN,i2-i1,VCHAR);
+        for (i=0; i<i2-i1-1; i++) ((char *)vdptr(rt->vv))[i] = z[i1+i+1];
+        ((char *)vdptr(rt->vv))[i2-i1-1] = '\0';
+        return(va);
+    }
+    
+    if (isname(z,i1,i2))
+    { tmp = z[i2+1]; z[i2+1] = '\0';
+        if (dum) /* search for dummies */
+        { for (j=0; j<lf.mi[MDIM]; j++)
+            if (strcmp(&z[i1],lf.xname[j])==0)
+            { rt = cmdptr(va,inum);
+                rt->cmd = 'x';
+                rt->m = j;
+                z[i2+1] = tmp;
+                return(va);
+            }
+        }
+        n = 0;
+        v = findvar(&z[i1],1,&n);
+        z[i2+1] = tmp;
+        if (v==NULL) return(va);
+        rt = cmdptr(va,inum);
+        rt->cmd = 'v';
+        rt->vv = v;
+        return(va);
+    }
+    
+    if (isfunction(z,i1,i2))
+    {
+        /* build the argument list */
+        ar = i2;
+        al = matchlf(z,i1,i2,'(',')');
+        j = al+1;
+        nargs = 0;
+        
+        if (ar>al+1)
+        { i = al;
+            while (j<=ar)
+            { if (z[j]=='(') j = matchrt(z,j,ar-1,'(',')')+1;
+                if (z[j]=='[') j = matchrt(z,j,ar-1,'[',']')+1;
+                if (lf_error) return(va);
+                if ((z[j]==')') | (z[j]==','))
+                { setnext(va,n,nargs,vlength(va));
+                    va = arbuild(z,i+1,j-1,va,k,dum);
+                    nargs++; i = j;
+                }
+                j++;
+            }
+        }
+        rt = cmdptr(va,inum);
+        rt->m = nargs;
+        
+        rargs = ar_setfunction(rt,&z[i1],al-i1);
+        if (rargs != nargs)
+            ERROR(("arbuild: wrong number of arguments, %s",&z[i1]));
+        return(va);
+    }
+    
+    rt = cmdptr(va,inum);
+    
+    if (issubset(z,i1,i2))
+    { rt->cmd = 'U';
+        al = matchlf(z,i1,i2,'[',']');
+        setnext(va,n,0,vlength(va));
+        va = arbuild(z,i1,al-1,va,k,dum);
+        if (lf_error) return(va);
+        setnext(va,n,1,vlength(va));
+        va = arbuild(z,al+1,i2-1,va,k,dum);
+        return(va);
+    }
+    
+    /* that leaves operators */
+    
+    i = isoperator(z,i1,i2);
+    if (i >= i1)
+    { rt->cmd = 'O';
+        rt->f = NULL;
+        al = i-1; ar = i+1;
+        if (z[i]==',') rt->cmd = 'C';
+        if (z[i]=='>')
+        { rt->f = vgt;
+            if (z[i-1]=='<') { rt->f = vne; al--; }
+        }
+        if (z[i]=='<') rt->f = vlt;
+        if (z[i]=='=')
+        { rt->f = veq;
+            if (z[i-1]=='=') al--;
+            if (z[i-1]=='<') { rt->f = vle; al--; }
+            if (z[i-1]=='>') { rt->f = vge; al--; }
+            if (z[i-1]=='!') { rt->f = vne; al--; }
+        }
+        if (z[i]=='+') rt->f = vadd;
+        if (z[i]=='-') rt->f = vsub;
+        if (z[i]=='*') rt->f = vmul;
+        if (z[i]=='/') rt->f = vdiv;
+        if (z[i]==':') rt->cmd = ':';
+        if (z[i]=='^') rt->f = vpow;
+        
+        setnext(va,n,0,vlength(va));
+        va = arbuild(z,i1,al,va,k,dum);
+        if (lf_error) return(va);
+        setnext(va,n,1,vlength(va));
+        va = arbuild(z,ar,i2,va,k,dum);
+        return(va);
+    }
+    
+    ERROR(("arbuild: unknown expression %s",z));
+    return(va);
+}
+
+vari *vevop(l,r,f)
+vari *l, *r;
+double (*f)();
+{ INT i, n;
+    vari *v;
+    double z;
+    if ((l==NULL) | (r==NULL)) return(NULL);
+    n = vlength(l);
+    if (n<vlength(r)) n = vlength(r);
+    v = createvar("_vevop",STHIDDEN,n,VDOUBLE);
+    if (lf_error) return(NULL);
+    for (i=0; i<n; i++)
+    { z = f(vitem(l,i),vitem(r,i));
+        vassn(v,i,z);
+    }
+    deleteifhidden(l);
+    deleteifhidden(r);
+    return(v);
+}
+
+vari *vcat(v1,v2)
+vari *v1, *v2;
+{ INT i, n;
+    vari *v;
+    n = vlength(v1) + vlength(v2);
+    v = createvar("_vcat",STHIDDEN,n,VDOUBLE);
+    if (lf_error) return(NULL);
+    for (i=0; i<vlength(v1); i++) vassn(v,i,vitem(v1,i));
+    for (i=0; i<vlength(v2); i++) vassn(v,i+vlength(v1),vitem(v2,i));
+    deleteifhidden(v1);
+    deleteifhidden(v2);
+    return(v);
+}
+
+vari *vrvec(v1,f)
+vari *v1;
+double (*f)();
+{ vari *v;
+    INT i, n;
+    n = (INT)vitem(v1,0);
+    v = createvar("_vrvec",STHIDDEN,n,VDOUBLE);
+    if (lf_error) return(NULL);
+    for (i=0; i<n; i++) vassn(v,i,f());
+    deleteifhidden(v1);
+    return(v);
+}
+
+vari *vrsca(v,f)
+vari *v;
+double (*f)();
+{ vari *z;
+    if (v==NULL) return(NULL);
+    z = createvar("_vrsca",STHIDDEN,1,VDOUBLE);
+    if (lf_error) return(NULL);
+    vassn(z,(INT)0,f(v));
+    deleteifhidden(v);
+    return(z);
+}
+
+vari *vrve2(v1,a1,f)
+vari *v1, *a1;
+double (*f)();
+{ vari *v;
+    int i, n;
+    n = vitem(v1,0);
+    v = createvar("_vrvec",STHIDDEN,n,VDOUBLE);
+    if (lf_error) return(NULL);
+    for (i=0; i<n; i++) vassn(v,i,f(vitem(a1,i)));
+    //deleteifhidden(v1,a1);
+    return(v);
+}
+
+vari *vvec1(l,f)
+vari *l;
+double (*f)();
+{ vari *v;
+    INT i;
+    if (l==NULL) 
+    {
+        ERROR(("vvec1 recieved NULL variable\n"));  
+        return NULL;
+    }
+    v = createvar("_vvec1",STHIDDEN,l->n,VDOUBLE);
+    if (lf_error) return(NULL);
+    for (i=0; i<vlength(l); i++) vassn(v,i,f(vitem(l,i)));
+    deleteifhidden(l);
+    return(v);
+}
+
+vari *vrsamp(v1,v2)
+vari *v1, *v2;
+{ vari *v;
+    int i, n;
+    n = vitem(v2,0);
+    v = createvar("_vrsamp",STHIDDEN,n,VDOUBLE);
+    if (lf_error) return(NULL);
+    for (i=0; i<n; i++) vassn(v,i,rsample(v1));
+    deleteifhidden(v1);
+    deleteifhidden(v2);
+    return(v);
+}
+
+vari *vrseq(v1,v2,v3)
+vari *v1, *v2, *v3;
+{ vari *v;
+    double beg, end;
+    int i, n;
+    n = vitem(v3,0);
+    v = createvar("_vrseq",STHIDDEN,n,VDOUBLE);
+    if (lf_error) return(NULL);
+    beg = vitem(v1,0);
+    end = vitem(v2,0);
+    for (i=0; i<n; i++) vassn(v,i,vseq(beg,end,i,n));
+    deleteifhidden(v1);
+    deleteifhidden(v2);
+    deleteifhidden(v3);
+    return(v);
+}
+
+vari *vrse2(v1,v2)
+vari *v1, *v2;
+{ vari *v;
+    double beg, end;
+    int i, n;
+    beg = vitem(v1,0);
+    end = vitem(v2,0);
+    n = (beg<=end) ? end-beg+1 : beg-end+1;
+    v = createvar("_vrse2",STHIDDEN,n,VDOUBLE);
+    if (lf_error) return(NULL);
+    if (beg<=end) for (i=0; i<n; i++) vassn(v,i,beg+i);
+    else for (i=0; i<n; i++) vassn(v,i,beg-i);
+    deleteifhidden(v1);
+    deleteifhidden(v2);
+    return(v);
+}
+
+vari *vsubset(v1,v2)
+vari *v1, *v2;
+{ vari *v;
+    int i, n;
+    n = v2->n;
+    v = createvar("_vsubs",STHIDDEN,n,VDOUBLE);
+    for (i=0; i<n; i++) vassn(v,i,vitem(v1,vitem(v2,i)-1));
+    deleteifhidden(v1);
+    deleteifhidden(v2);
+    return(v);
+}
+
+vari *vareval(v,k)
+vari *v;
+INT k;
+{ INT n;
+    arstruct *rt;
+    rt = viptr(v,k);
+    switch (rt->cmd)
+    { case 'e': return(NULL);
+        case 'v': return(rt->vv);
+        case 'O': return(vevop(vareval(v,rt->nx[0]),vareval(v,rt->nx[1]),rt->f));
+        case 'C': return(vcat(vareval(v,rt->nx[0]),vareval(v,rt->nx[1])));
+        case 'D':
+            n = 1;
+            rt->vv = createvar("_vevcon",STHIDDEN,n,VDOUBLE);
+            if (lf_error) return(NULL);
+            vassn(rt->vv,0,rt->x);
+            return(rt->vv);
+        case 'G': return(vrvec(vareval(v,rt->nx[0]),rt->f));
+        case 'H': return(vrve2(vareval(v,rt->nx[0]),vareval(v,rt->nx[1]),rt->f));
+        case 'f': return(vvec1(vareval(v,rt->nx[0]),rt->f));
+        case 'M': return(vrsamp(vareval(v,rt->nx[0]),vareval(v,rt->nx[1])));
+        case 'Q': return(vrseq(vareval(v,rt->nx[0]),vareval(v,rt->nx[1]),
+                               vareval(v,rt->nx[2])));
+        case ':': return(vrse2(vareval(v,rt->nx[0]),vareval(v,rt->nx[1])));
+        case 'S': return(vrsca(vareval(v,rt->nx[0]),rt->f));
+        case 'U': return(vsubset(vareval(v,rt->nx[0]),vareval(v,rt->nx[1])));
+        case 'R': return(vrep(vareval(v,rt->nx[0]),vareval(v,rt->nx[1])));
+        case 'Z': return(vfitted(RMEAN));
+        case 'Y': return(vfitted(RDEV));
+        case 's': return(rt->vv);
+        case 'x':
+            ERROR(("Dummy in vareval"));
+            return(NULL);
+        default : ERROR(("vareval: unknown command %c",rt->cmd));
+    }
+    return(NULL);
+}
+
+vari *saveresult(v,name,status)
+vari *v;
+int status;
+char *name;
+{ vari *vr;
+    if (v==NULL) return(NULL);
+    
+    vr = v;
+    if (v->stat != STHIDDEN)
+    { vr = createvar("_result",STHIDDEN,vlength(v),vmode(v));
+        memcpy(vdptr(vr),vdptr(v),vbytes(vlength(v),vmode(v)));
+    }
+    
+    if (name!=NULL)
+    { setvarname(vr,name);
+        vr->stat = status;
+    }
+    return(vr);
+}
+
+vari *varith(z,name,status)
+char *z, *name;
+int status;
+{ vari *v, *va;
+    va = arbuild(z,0,strlen(z)-1,NULL,1,0);
+    if (lf_error) return(NULL);
+    v = vareval(va,0);
+    deletevar(va);
+    
+    v = saveresult(v,name,status);
+    return(v);
+}
+
+double dareval(v,k,x)
+vari *v;
+INT k;
+double *x;
+{ arstruct *rt;
+    rt = viptr(v,k);
+    switch (rt->cmd)
+    { case 'e': return(0.0);
+        case 'v': return(vitem(rt->vv,0));
+        case 'O': return(rt->f(dareval(v,rt->nx[0],x),dareval(v,rt->nx[1],x)));
+        case 'P': return(rt->f(0.0,dareval(v,rt->nx[1],x)));
+        case 'D': return(rt->x);
+        case 'G': return(rt->f());
+        case 'H': return(rt->f(dareval(v,rt->nx[1],x)));
+        case 'f': return(rt->f(dareval(v,rt->nx[0],x)));
+        case 'M': return(rsample(vareval(v,rt->nx[0])));
+        case 'x': return(x[rt->m]);
+        case 'U': return(vitem(vareval(v,rt->nx[0]),(int)dareval(v,rt->nx[1],x)-1));
+        case 'Q': ERROR(("sequence in dareval"));
+            return(0.0);
+        default : ERROR(("dareval: unknown command %c",rt->cmd));
+    }
+    return(0.0);
+}
+
+double darith(z)
+char *z;
+{ vari *va;
+    double y;
+    va = arbuild(z,0,strlen(z)-1,NULL,1,0);
+    y = dareval(va,0,NULL);
+    deletevar(va);
+    return(y);
+}
+
+INT arvect(z,res,c,a) /* c = no of items to read */
+char *z;
+INT c, a;
+double *res;
+{ INT i;
+    vari *v;
+    
+    if (z==NULL) return(0);
+    
+    v = varith(z,"arvect",STPLOTVAR);
+    if (v==NULL || lf_error) 
+    {
+        return(0);   
+    }
+    deletevar(v);
+    
+    for (i=0; (i<c) & (i<vlength(v)); i++) res[i] = vitem(v,i);
+    if (i<c) /* insufficient items */
+    { switch(a)
+        { case 0: /* pad */
+                for (; i<c; i++) res[i] = res[0];
+                return(c);
+            case 1: return(i); /* ignore */
+            case 2:
+                ERROR(("Insufficient items: %s",z));
+                return(i);
+        }
+    }
+    return(i);
+}
+
+#endif
diff --git a/src/locfit/band.c b/src/locfit/band.c
new file mode 100644
index 0000000..3e12927
--- /dev/null
+++ b/src/locfit/band.c
@@ -0,0 +1,383 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include "local.h"
+
+extern lfit lf;
+extern design des;
+extern void fitoptions();
+
+static double hmin, gmin, sig2, pen, vr, tb;
+
+#define BGCV 1
+#define BCP  2
+#define BIND 3
+
+INT procvbind(des,lf,v)
+design *des;
+lfit *lf;
+INT v;
+{ double s0, s1, bi;
+    int i, ii, k;
+    k = procvraw(des,lf,v);
+    wdiag(lf,des,des->wd,0,1,0);
+    s0 = s1 = 0.0;
+    for (i=0; i<des->n; i++)
+    { ii = des->ind[i];
+        s0+= prwt(lf,ii)*des->wd[i]*des->wd[i];
+        bi = prwt(lf,ii)*fabs(des->wd[i]*ipower(des->di[ii],lf->mi[MDEG]+1));
+        s1+= bi*bi;
+    }
+    vr += s0;
+    tb += s1;
+    return(k);
+}
+
+double bcri(h,c,cri)
+double h;
+INT c, cri;
+{ double num, den;
+    INT (*pv)();
+    lf.dp[c] = h;
+    if ((cri&63)==BIND)
+    { pv = procvbind;
+        vr = tb = 0.0;
+    }
+    else pv = procv;
+    if (cri<64) startlf(&des,&lf,pv,0);
+    switch(cri&63)
+    { case BGCV:
+            ressumm(&lf,&des);
+            num = -2*lf.mi[MN]*lf.dp[DLK];
+            den = lf.mi[MN]-lf.dp[DT0];
+            return(num/(den*den));
+        case BCP:
+            ressumm(&lf,&des);
+            return(-2*lf.dp[DLK]/sig2-lf.mi[MN]+pen*lf.dp[DT0]);
+        case BIND:
+            return(vr+pen*pen*tb);
+    } 
+    ERROR(("bcri: unknown criterion"));
+    return(0.0);
+}
+
+void bsel2(h0,g0,ifact,c,cri)
+double h0, g0, ifact;
+INT c, cri;
+{ INT done, inc;
+    double h1, g1;
+    h1 = h0; g1 = g0;
+    done = inc = 0;
+    while (!done)
+    { h1 *= 1+ifact;
+        g0 = g1;
+        g1 = bcri(h1,c,cri);
+        if (g1<gmin) { hmin = h1; gmin = g1; }
+        if (g1>g0) inc++; else inc = 0;
+        switch(cri)
+        { case BIND:
+                done = (inc>=4) & (vr<lf.nv);
+                break;
+            default:
+                done = (inc>=4);
+        }
+    }
+}
+
+void bsel3(h0,g0,ifact,c,cri)
+double h0, g0, ifact;
+INT c, cri;
+{ double h1, g1;
+    INT i;
+    hmin = h0; gmin = g0;
+    for (i=-1; i<=1; i++) if (i!=0)
+    { h1 = h0*(1+i*ifact);
+        g1 = bcri(h1,c,cri);
+        if (g1<gmin) { hmin = h1; gmin = g1; }
+    }
+    return;
+}
+
+void bselect(c,cri,pn)
+INT c, cri;
+double pn;
+{ double h0, g0, ifact;
+    INT i;
+    pen = pn;
+    if (cri==BIND) pen /= factorial((int)lf.mi[MDEG]+1);
+    hmin = h0 = lf.dp[c];
+    if (h0==0) ERROR(("bselect: initial bandwidth is 0"));
+    if (lf_error) return;
+    sig2 = 1.0;
+    
+    gmin = g0 = bcri(h0,c,cri);
+    if (cri==BCP)
+    { sig2 = lf.dp[DRV];
+        g0 = gmin = bcri(h0,c,cri+64);
+    }
+    
+    ifact = 0.3;
+    bsel2(h0,g0,ifact,c,cri);
+    
+    for (i=0; i<5; i++)
+    { ifact = ifact/2;
+        bsel3(hmin,gmin,ifact,c,cri);
+    }
+    lf.dp[c] = hmin;
+    startlf(&des,&lf,procv,0);
+    ressumm(&lf,&des);
+}
+
+double compsda(x,h,n)
+double *x, h;
+INT n;
+/* n/(n-1) * int( fhat''(x)^2 dx ); bandwidth h */
+{ INT i, j;
+    double ik, sd, z;
+    ik = wint(1,NULL,0,WGAUS);
+    sd = 0;
+    
+    for (i=0; i<n; i++)
+        for (j=i; j<n; j++)
+        { z = (x[i]-x[j])/h;
+            sd += (2-(i==j))*Wconv4(z,WGAUS)/(ik*ik);
+        }
+    sd = sd/(n*(n-1)*h*h*h*h*h);
+    return(sd);
+}
+
+double widthsj(x,lambda,n)
+double *x, lambda;
+INT n;
+{ double ik, a, b, td, sa, z, c, c1, c2, c3;
+    INT i, j;
+    a = GFACT*0.920*lambda*exp(-log((double)n)/7)/SQRT2;
+    b = GFACT*0.912*lambda*exp(-log((double)n)/9)/SQRT2;
+    ik = wint(1,NULL,0,WGAUS);
+    
+    td = 0;
+    for (i=0; i<n; i++)
+        for (j=i; j<n; j++)
+        { z = (x[i]-x[j])/b;
+            td += (2-(i==j))*Wconv6(z,WGAUS)/(ik*ik);
+        }
+    
+    td = -td/(n*(n-1));
+    j = 2.0;
+    c1 = Wconv4(0.0,WGAUS);
+    c2 = wint(1,&j,1,WGAUS);
+    c3 = Wconv(0.0,WGAUS);  /* (2*c1/(c2*c3))^(1/7)=1.357 */
+    sa = compsda(x,a,n);
+    c = b*exp(log(c1*ik/(c2*c3*GFACT*GFACT*GFACT*GFACT)*sa/td)/7)*SQRT2;
+    return(c);
+}
+
+void kdecri(x,h,res,c,k,ker,n)
+double *x, h, *res, c;
+INT k, ker, n;
+{ INT i, j;
+    double degfree, dfd, pen, s, r0, r1, d0, d1, ik, wij;
+    
+    if (h<=0) WARN(("kdecri, h = %6.4f",h));
+    
+    res[0] = res[1] = 0.0;
+    ik = wint(1,NULL,0,ker);
+    switch(k)
+    { case 1: /* aic */
+            pen = 2.0;
+            for (i=0; i<n; i++)
+            { r0 = d0 = 0.0;
+                for (j=0; j<n; j++)
+                { s = (x[i]-x[j])/h;
+                    r0 += W(s,ker);
+                    d0 += s*s*Wd(s,ker);
+                }
+                d0 = -(d0+r0)/(n*h*h*ik);  /* d0 = d/dh fhat(xi) */
+                r0 /= n*h*ik;              /* r0 = fhat(xi) */
+                res[0] += -2*log(r0)+pen*W(0.0,ker)/(n*h*ik*r0);
+                res[1] += -2*d0/r0-pen*W(0.0,ker)/(n*h*ik*r0)*(d0/r0+1.0/h);
+            }
+            return;
+        case 2: /* ocv */
+            for (i=0; i<n; i++)
+            { r0 = 0.0; d0 = 0.0;
+                for (j=0; j<n; j++) if (i!=j)
+                { s = (x[i]-x[j])/h;
+                    r0 += W(s,ker);
+                    d0 += s*s*Wd(s,ker);
+                }
+                d0 = -(d0+r0)/((n-1)*h*h);
+                r0 = r0/((n-1)*h);
+                res[0] -= log(r0);
+                res[1] -= d0/r0;
+            }
+            return;
+        case 3: /* lscv */
+            r0 = r1 = d0 = d1 = degfree = 0.0;
+            for (i=0; i<n; i++)
+            { dfd = 0.0;
+                for (j=0; j<n; j++)
+                { s = (x[i]-x[j])/h;
+                    wij = W(s,ker);
+                    dfd += wij;
+                    /* 
+                     *  r0 = \int fhat * fhat = sum_{i,j} W*W( (Xi-Xj)/h ) / n^2 h.
+                     *  d0 is it's derivative wrt h.
+                     *
+                     *  r1 = 1/n sum( f_{-i}(X_i) ).
+                     *  d1 is  it's derivative wrt h.
+                     *
+                     *  degfree = sum_i ( W_0 / sum_j W( (Xi-Xj)/h ) ) is fitted d.f.
+                     */
+                    r0 += Wconv(s,ker);
+                    d0 += -s*s*Wconv1(s,ker);
+                    if (i != j)
+                    { r1 += wij;
+                        d1 += -s*s*Wd(s,ker);
+                    }
+                }
+                degfree += 1.0/dfd;
+            }
+            d1 -= r1;
+            d0 -= r0;
+            res[0] = r0/(n*n*h*ik*ik)   - 2*r1/(n*(n-1)*h*ik);
+            res[1] = d0/(n*n*h*h*ik*ik) - 2*d1/(n*(n-1)*h*h*ik);
+            res[2] = degfree;
+            return;
+        case 4: /* bcv */
+            r0 = d0 = 0.0;
+            for (i=0; i<n; i++)
+                for (j=i+1; j<n; j++)
+                { s = (x[i]-x[j])/h;
+                    r0 += 2*Wconv4(s,ker);
+                    d0 += 2*s*Wconv5(s,ker);
+                }
+            d0 = (-d0-r0)/(n*n*h*h*ik*ik);
+            r0 = r0/(n*n*h*ik*ik);
+            j = 2.0;
+            d1 = wint(1,&j,1,ker);
+            r1 = Wconv(0.0,ker);
+            res[0] = (d1*d1*r0/4+r1/(n*h))/(ik*ik);
+            res[1] = (d1*d1*d0/4-r1/(n*h*h))/(ik*ik);
+            return;
+        case 5: /* sjpi */
+            s = c*exp(5*log(h)/7)/SQRT2;
+            d0 = compsda(x,s,n);
+            res[0] = d0; /* this is S(alpha) in SJ */
+            res[1] = exp(log(Wikk(WGAUS,0)/(d0*n))/5)-h;
+            return;
+        case 6: /* gas-k-k */
+            s = exp(log(1.0*n)/10)*h;
+            d0 = compsda(x,s,n);
+            res[0] = d0;
+            res[1] = exp(log(Wikk(WGAUS,0)/(d0*n))/5)-h;
+            return;
+    }
+    ERROR(("kdecri: what???"));
+    return;
+}
+
+double esolve(x,j,h0,h1,k,c,ker,n)
+double *x, h0, h1, c;
+INT j, k, ker, n;
+{ 
+    double h[7], d[7], r[7], res[4], min, minh, fact;
+    INT i, nc;
+    memset(h, 0, sizeof(h));
+    memset(d, 0, sizeof(d));
+    memset(r, 0, sizeof(r));
+    
+    min = 1.0e30; minh = 0.0;
+    fact = 1.00001;
+    h[6] = h0; kdecri(x,h[6],res,c,j,ker,n);
+    r[6] = res[0]; d[6] = res[1];
+    if (lf_error) return(0.0);
+    nc = 0;
+    for (i=0; i<k; i++)
+    { h[5] = h[6]; r[5] = r[6]; d[5] = d[6];
+        h[6] = h0*exp((i+1)*log(h1/h0)/k);
+        kdecri(x,h[6],res,c,j,ker,n);
+        r[6] = res[0]; d[6] = res[1];
+        if (lf_error) return(0.0);
+        if (d[5]*d[6]<0)
+        { h[2] = h[0] = h[5]; d[2] = d[0] = d[5]; r[2] = r[0] = r[5];
+            h[3] = h[1] = h[6]; d[3] = d[1] = d[6]; r[3] = r[1] = r[6];
+            while ((h[3]>fact*h[2])|(h[2]>fact*h[3]))
+            { h[4] = h[3]-d[3]*(h[3]-h[2])/(d[3]-d[2]);
+                if ((h[4]<h[0]) | (h[4]>h[1])) h[4] = (h[0]+h[1])/2;
+                kdecri(x,h[4],res,c,j,ker,n);
+                r[4] = res[0]; d[4] = res[1];
+                if (lf_error) return(0.0);
+                h[2] = h[3]; h[3] = h[4];
+                d[2] = d[3]; d[3] = d[4];
+                r[2] = r[3]; r[3] = r[4];
+                if (d[4]*d[0]>0) { h[0] = h[4]; d[0] = d[4]; r[0] = r[4]; }
+                else { h[1] = h[4]; d[1] = d[4]; r[1] = r[4]; }
+            }
+            if (j>=4) return(h[4]); /* first min for BCV etc */
+            if (r[4]<=min) { min = r[4]; minh = h[4]; }
+            nc++;
+        }
+    }
+    if (nc==0) minh = (r[5]<r[6]) ? h0 : h1;
+    return(minh);
+}
+
+void kdeselect(band,x,ind,h0,h1,meth,nm,ker,n)
+double h0, h1, *band, *x;
+INT *ind, *meth, nm, ker, n;
+{ double scale, c;
+    INT i, k;
+    k = n/4;
+    for (i=0; i<n; i++) ind[i] = i;
+    scale = kordstat(x,n+1-k,n,ind) - kordstat(x,k,n,ind);
+    c = widthsj(x,scale,n);
+    for (k=0; k<nm; k++)
+        band[k] = esolve(x,meth[k],h0,h1,10,c,ker,n);
+}
+
+#ifdef CVERSION
+void band(v)
+vari *v;
+{ INT i, c, cri;
+    double pen;
+    char *z;
+    fitoptions(&lf,v,0);
+    
+    c = DALP;
+    i = getarg(v,"comp",1);
+    if (i>0)
+    { z = argval(v,i);
+        if (z[0]=='h') c = DFXH;
+    }
+    
+    cri = BGCV;
+    i = getarg(v,"bcri",1);
+    if (i>0)
+    { z = argval(v,i);
+        if (z[0]=='c') cri = BCP;
+        if (z[0]=='i') cri = BIND;
+    }
+    
+    pen = 2.0;
+    i = getarg(v,"pen",1);
+    if (i>0)
+        pen = darith(argval(v,i));
+    
+    bselect(c,cri,pen);
+}
+#endif
+
+#ifdef SVERSION
+void slscv(x,n,h,z)
+double *x, *h, *z;
+int *n;
+{ INT i;
+    double res[4];
+    kdecri(x,*h,res,0.0,3,WGAUS,*n);
+    z[0] = res[0];
+    z[1] = res[2];
+}
+#endif
diff --git a/src/locfit/c_args.c b/src/locfit/c_args.c
new file mode 100644
index 0000000..aeb332d
--- /dev/null
+++ b/src/locfit/c_args.c
@@ -0,0 +1,89 @@
+/*
+ *   Copyright (c) 1996-2000 Lucent Technologies.
+ *   See README file for details.
+ *
+ *   Functions for interpreting and manipulating command line
+ *   arguments.
+ */
+
+#include "local.h"
+
+char *argval(vari *v,int i)
+{ if (i<0) return(NULL);
+  return(((carg *)viptr(v,i))->val);
+}
+
+int getarg(v,s,un) /* un=1: unnamed permitted un=2: next unused */
+vari *v;
+int un;
+char *s;
+{ int i;
+  if (un==2)
+  { for (i=1; i<vlength(v); i++)
+    { if (!argused(v,i))
+      { setused(v,i);
+        return(i);
+      }
+    }
+    return(0);
+  }
+  for (i=1; i<vlength(v); i++)
+  { if ((!argused(v,i)) && (argarg(v,i)!=NULL))
+    { if (strcmp(argarg(v,i),s)==0)
+      { setused(v,i);
+        return(i);
+      }
+    }
+  }
+  if (!un) return(0);
+  for (i=1; i<vlength(v); i++)
+  { if ((!argused(v,i)) && (argarg(v,i)==NULL))
+    { setused(v,i);
+      return(i);
+    }
+  }
+  return(0);
+}
+
+char *getargval(vari *v, char *s, int un)
+{ int i;
+  i = getarg(v,s,un);
+  if (i==0) return(NULL);
+  return(argval(v,i));
+}
+
+int readilist(ivec,z,n0,n1,pad)
+char *z;
+int *ivec, n0, n1, pad;
+{ int i, n, nd;
+  n = 1;
+  for (i=0; i<strlen(z); i++)
+    if (z[i]==',') { z[i]=' '; n++; }
+  if (n>n1)
+  { WARN(("too many items in ilist"));
+    n = n1;
+  }
+  for (i=0; i<n; i++)
+  { nd = sscanf(z,"%d",&ivec[i]);
+    //if (nd!=1) WARN(("problem scaning ilist %s",&ivec[i]));
+    if (i<n-1) while (*z!=' ') z++;
+  }
+  if (pad)
+  { for (i=n; i<n1; i++) ivec[i] = ivec[0]; 
+    n = n1;
+  }
+  if (n<n0) WARN(("too few items in ilist"));
+  return(n);
+}
+
+int getlogic(v,i)
+vari *v;
+int i;
+{ char *z;
+  if (argarg(v,i)==NULL) return(1);
+  z = argval(v,i);
+  if ((z[0]=='T') | (z[0]=='t') | (z[0]=='1')) return(1);
+  if ((z[0]=='F') | (z[0]=='f') | (z[0]=='0')) return(0);
+  ERROR(("getlogic: invalid logical argument %s",z));
+  return(0);
+}
diff --git a/src/locfit/c_plot.c b/src/locfit/c_plot.c
new file mode 100644
index 0000000..c902efa
--- /dev/null
+++ b/src/locfit/c_plot.c
@@ -0,0 +1,471 @@
+/*
+ *   Copyright (c) 1996-2000 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include "local.h"
+
+vari *growvar(vari* vold, int n);
+
+plots *cpl, pl[MAXWIN];
+extern device devps, devwin;
+INT curwin;
+char *psfn;
+extern lfit lf;
+extern pplot pp;
+extern char *curstr;
+
+plots *get_graphics_window(v)
+vari *v;
+{ int win_no;
+  char *w;
+
+  w = getargval(v,"win",0);
+  if (w != NULL)
+  { sscanf(w,"%d",&win_no);
+    if ((win_no<0) | (win_no>=MAXWIN))
+    { WARN(("Invalid window %d",win_no));
+    }
+    else
+      curwin = win_no;
+  }
+  return( &pl[curwin] );
+}
+
+char *settype(xyz,type,def)
+plxyz *xyz;
+char *type, def;
+{ if ((type==NULL) || (strlen(type)==0))
+  { xyz->type = def;
+    return(NULL);
+  }
+  xyz->type = type[0];
+  return(&type[1]);
+}
+
+char *pvarname(xyz,c,vn)
+plxyz *xyz;
+char c;
+varname vn;
+{ sprintf(vn,"_plv%d%c",xyz->id,c);
+  return(vn);
+}
+
+plxyz *nextxyz(win,add,ck)
+plots *win;
+INT add, ck;
+{ plxyz *xyz;
+  vari *v;
+  varname vn;
+
+  if (!add)
+  { sprintf(vn,"_xyz%d",curwin);
+    v = win->xyzs = createvar(vn,STSYSTEM,5,VXYZ);
+    v->n = 0;
+    win->xlab[0] = win->ylab[0] = win->zlab[0] = '\0';
+  }
+  else
+    v = win->xyzs = growvar(win->xyzs,vlength(win->xyzs)+ck);
+
+  xyz = (plxyz *)viptr(v,vlength(v));
+  xyz->id = (vlength(v) << 4) + win->id;
+  xyz->pch = 1;
+  v->n++;
+  return(xyz);
+}
+
+void plotopt(v,re)
+vari *v;
+INT re;
+{ INT i, j, h, w;
+  double z[2];
+  char *fmt, *ty;
+  plxyz *xyz;
+
+  cpl = get_graphics_window(v);
+
+  if (!re)
+  { cpl->main[0] = '\0';
+    cpl->xl[0] = cpl->xl[1] = cpl->yl[0]
+               = cpl->yl[1] = cpl->zl[0] = cpl->zl[1] = 0.0;
+    cpl->nsl = 0;
+  }
+
+  arvect(getargval(v,"xlim",1), cpl->xl, 2, 2);
+  arvect(getargval(v,"ylim",1), cpl->yl, 2, 2);
+  arvect(getargval(v,"zlim",1), cpl->zl, 2, 2);
+
+  i = getarg(v,"main",1);
+  if (i>0) { strcpy(cpl->main,argval(v,i)); strip(cpl->main); }
+  i = getarg(v,"xlab",1);
+  if (i>0) { strcpy(cpl->xlab,argval(v,i)); strip(cpl->xlab); }
+  i = getarg(v,"ylab",1);
+  if (i>0) { strcpy(cpl->ylab,argval(v,i)); strip(cpl->ylab); }
+  i = getarg(v,"zlab",1);
+  if (i>0) { strcpy(cpl->zlab,argval(v,i)); strip(cpl->zlab); }
+
+  if ( arvect(getargval(v,"view",1), z, 2, 2) == 2 )
+  { cpl->theta=z[0];
+    cpl->phi  =z[1];
+  }
+
+  fmt = getargval(v,"fmt",1);
+  if (fmt==NULL) fmt = "xwin";
+
+  i = getarg(v,"split",1);
+  if (i>0)
+    cpl->nsl = arvect(argval(v,i),cpl->sl,10,1);
+  i = getarg(v,"h",1);
+  if (i>0) sscanf(argval(v,i),"%d",&h); else h = 0;
+  i = getarg(v,"w",1);
+  if (i>0) sscanf(argval(v,i),"%d",&w); else w = 0;
+
+  ty = getargval(v,"type",1);
+  if (ty != NULL)
+  { for (j=0; j<vlength(cpl->xyzs); j++)
+    { xyz = (plxyz *)viptr(cpl->xyzs,j);
+      ty = settype(xyz,ty,xyz->type);
+    }
+  }
+  if (stm(fmt,"xwin",1)) { plotxwin(cpl,&devwin,curwin,w,h,0); return; }
+  if (stm(fmt,"win",1))  { plotxwin(cpl,&devwin,curwin,w,h,0); return; }
+  if (stm(fmt,"post",1))
+  { psfn = getargval(v,"file",1);
+    plotxwin(cpl,&devps,curwin,w,h,0);
+    return;
+  }
+}
+
+void pvari(cmd,xyz,win,ax)
+char *cmd, ax;
+plxyz *xyz;
+plots *win;
+{ vari *vv;
+  INT k;
+  varname vname;
+  vv = varith(cmd,pvarname(xyz,ax,vname),STPLOTVAR);
+  if (vv==NULL) return;
+  k = xyz->id>>4;
+  switch(ax)
+  { case 'x':
+      xyz->x = vv;
+      if (k==0) strcpy(win->xlab,cmd);
+      return;
+    case 'y':
+      xyz->y = vv;
+      if (k==0) strcpy(win->ylab,cmd);
+      return;
+    case 'z':
+      xyz->z = vv;
+      if (k==0) strcpy(win->zlab,cmd);
+      return;
+  }
+  ERROR(("pvari: unknown axis %c",ax));
+}
+
+void plotdata(v)
+vari *v;
+{ INT add, i, j, k;
+  plxyz *xyz = NULL, *xyz2 = NULL;
+  char *type;
+
+  cpl = get_graphics_window(v);
+
+  i = getarg(v,"add",0);
+  add = (i>0) ? getlogic(v,i) : 0;
+
+  type = getargval(v,"type",0);
+
+  i = getarg(v,"data",0);
+  if (i>0) doreaddata(argval(v,i),(INT)0);
+
+  i = getarg(v,"pch",0);
+  if (i>0) sscanf(argval(v,i),"%d",&xyz->pch);
+
+  xyz = nextxyz(cpl,add,2);
+  if (xyz==NULL) return;
+  xyz->x = xyz->y = xyz->z = NULL;
+  type = settype(xyz,type,'p');
+
+  i = getarg(v,"x",1);
+  j = getarg(v,"y",1);
+  k = getarg(v,"z",1);
+
+  if (!add) /* set the default view angle */
+  { cpl->theta = 45*( 1 - ((j==0)|(k==0)) - 3*(i==0) );
+    cpl->phi   = 45*( 1 + ((i==0)|(j==0)) - (k==0) );
+  }
+
+  if (i>0) pvari(argval(v,i),xyz,cpl,'x');
+  if (j>0) pvari(argval(v,j),xyz,cpl,'y');
+  if (k>0) pvari(argval(v,k),xyz,cpl,'z');
+
+  i = getarg(v,"x2",1);
+  j = getarg(v,"y2",1);
+  k = getarg(v,"z2",1);
+  if (i+j+k>0)
+  { xyz2= nextxyz(cpl,1,1);
+    if (xyz2==NULL) return;
+    xyz2->x = xyz->x;
+    xyz2->y = xyz->y;
+    xyz2->z = xyz->z;
+    type = settype(xyz2,type,'s');
+    if (i>0) pvari(argval(v,i),xyz2,cpl,'x');
+    if (j>0) pvari(argval(v,j),xyz2,cpl,'y');
+    if (k>0) pvari(argval(v,k),xyz2,cpl,'z');
+  }
+  if (lf_error) return;
+
+  cpl->ty |= PLDATA;
+
+  plotopt(v,add);
+}
+
+void plotfit(v)
+vari *v;
+{ INT add, d, dp, i = 0, j = 0, n, sef;
+  INT dt, mg[MXDIM], ix, iy;
+  double c, sd, xl[2*MXDIM], xll[2];
+  char cb;
+  varname vn;
+  plxyz *xyz, *xyzl, *xyzu, *xyzd;
+  char *type;
+
+  cpl = get_graphics_window(v);
+
+  i = getarg(v,"fit",1);
+  if (i>0) dosavefit(&lf,argval(v,i),"rb",(INT)0);
+  if (nofit()) ERROR(("plotfit: no fit to plot."));
+  if (lf_error) return;
+  dp = 0;
+
+  d = lf.mi[MDIM];
+  for (i=0; i<d; i++)
+  { j = getarg(v,lf.xname[i],0);
+    if (j>0)
+    { j = arvect(argval(v,j),xll,2,1);
+      if (j==1)
+        xl[i] = xl[i+d] = xll[0];
+      else
+      { xl[i] = xll[0];
+        xl[i+d] = xll[1];
+      }
+    }
+    else
+    { xl[i] = lf.fl[i];
+      xl[i+d] = lf.fl[i+d];
+      j = 2;
+    }
+    if (j==2)
+    { if (dp==2)
+      { xl[i] = xl[i+d] = (xl[i]+xl[i+d])/2;
+        WARN(("plotfit: fixing %s=%f",lf.xname[i],xl[i]));
+        j = 1;
+      }
+      if (dp==1) { iy = i; dp++; }
+      if (dp==0) { ix = i; dp++; }
+    }
+    mg[i] = 2-j;
+  }
+  if (dp<=0)
+  { ERROR(("No plot variables"));
+    return;
+  }
+  sef = 0; dt = 0;
+  i = getarg(v,"data",1);  if (i>0) dt =getlogic(v,i);
+  i = getarg(v,"band",1);  cb = (i>0) ? *argval(v,i) : 'n';
+
+  for (i=0; i<d; i++) mg[i] = 1;
+  if (dp==1)
+    mg[ix] = 100;
+  else
+    mg[ix] = mg[iy] = 50;
+  i = getarg(v,"m",1);
+  if (i>0) readilist(mg,argval(v,i),1,lf.mi[MDIM],1);
+
+  i = getarg(v,"add",1);
+  add = (i>0) ? getlogic(v,i) : 0;
+
+  type =  getargval(v,"type",1);
+
+  if ((lf.mi[MEV]==EDATA) | (lf.mi[MEV]==ECROS))
+    n = setpppoints(&pp,"fitp",mg,xl);
+  else
+    n = setpppoints(&pp,"grid",mg,xl);
+  pp.fit = createvar("_ppfit",STPLOTVAR,n,VDOUBLE);
+  if (cb=='n')
+    pp.se = NULL;
+  else
+    pp.se = createvar("_ppsef",STPLOTVAR,n,VDOUBLE);
+  if (lf_error) return;
+  cpreplot(&pp,v,cb);
+  if (lf_error) return;
+
+  xyz = nextxyz(cpl,add,4);
+  if (xyz==NULL) return;
+  /* set up first predictor variable */
+  xyz->x = pp.data[ix];
+  setvarname(xyz->x,pvarname(xyz,'x',vn));
+  strcpy(cpl->xlab,lf.xname[ix]);
+
+  /* set up second predictor variable */
+  if (dp==2)
+  { xyz->y = pp.data[iy];
+    setvarname(xyz->y,pvarname(xyz,'y',vn));
+    strcpy(cpl->ylab,lf.xname[iy]);
+  }
+  else
+  { xyz->y = NULL;
+    cpl->ylab[0] = '\0';
+  }
+
+  xyz->z = pp.fit;
+  setvarname(xyz->z,pvarname(xyz,'z',vn));
+  switch(lf.mi[MTG]&63)
+  { case TDEN: strcpy(cpl->zlab,"Density"); break;
+    case TRAT: strcpy(cpl->zlab,"Rate"); break;
+    case THAZ: strcpy(cpl->zlab,"Hazard"); break;
+    default: strcpy(cpl->zlab,lf.yname);
+  }
+  type = settype(xyz,type,(dp==1) ? 'l' : 'c');
+
+  if (pp.se!=NULL)
+  { xyzl = nextxyz(cpl,1,3); xyzu = nextxyz(cpl,1,2);
+    if ((xyzl!=NULL) & (xyzu!=NULL))
+    { sd = sqrt(lf.dp[DRV]);
+      xyzl->x = xyzu->x = xyz->x;
+      xyzl->y = xyzu->y = xyz->y;
+      xyzl->z = createvar(pvarname(xyzl,'z',vn),STPLOTVAR,n,VDOUBLE);
+      xyzu->z = createvar(pvarname(xyzu,'z',vn),STPLOTVAR,n,VDOUBLE);
+      if (lf_error) return;
+      c = docrit(v);
+      for (i=0; i<n; i++)
+      { vassn(xyzu->z,i,backtr(vitem(pp.fit,i)+c*vitem(pp.se,i),lf.mi,lf.nd));
+        vassn(xyzl->z,i,backtr(vitem(pp.fit,i)-c*vitem(pp.se,i),lf.mi,lf.nd));
+      }
+      type = settype(xyzl,type,(d==1) ? 'l' : 'c');
+      type = settype(xyzu,type,(d==1) ? 'l' : 'c');
+    }
+    deletevar(pp.se);
+  }
+  if (pp.wh==PCOEF)
+    for (i=0; i<vlength(xyz->z); i++)
+      vassn(xyz->z,i,backtr(vitem(pp.fit,i),lf.mi,lf.nd));
+  if (dt)
+  {
+    recondat(0,&n);
+    if (lf_error) return;
+    xyzd = nextxyz(cpl,1,1);
+    if (xyzd!=NULL)
+    { xyzd->x = createvar(pvarname(xyzd,'x',vn),STPLOTVAR,n,VDOUBLE);
+      for (i=0; i<n; i++) vassn(xyzd->x,i,datum(&lf,ix,i));
+      if (d==2)
+      { xyzd->y = createvar(pvarname(xyzd,'y',vn),STPLOTVAR,n,VDOUBLE);
+        for (i=0; i<n; i++) vassn(xyzd->y,i,datum(&lf,iy,i));
+      }
+      else xyzd->y = NULL;
+      xyzd->z = createvar(pvarname(xyzd,'z',vn),STPLOTVAR,n,VDOUBLE);
+      for (i=0; i<n; i++)
+        vassn(xyzd->z,i,((lf.mi[MTG]&63)==TGAUS) ? resp(&lf,i) : resp(&lf,i)/prwt(&lf,i));
+      type = settype(xyzd,type,'p');
+    }
+  }
+
+  /* now, set default view angle */
+  if (!add)
+  { if (dp==1) { cpl->theta = 0; cpl->phi = 90; } /* x-z axis */
+    else
+    { if (xyz->type=='w')
+        cpl->theta = cpl->phi = 45; /* wireframes */
+      else
+        cpl->theta = cpl->phi = 0;  /* x-y plot; e.g. for contours */
+    }
+  }
+  if (lf_error) return;
+  cpl->ty |= PLFIT;
+  if (dt) cpl->ty |= PLDATA;
+
+  plotopt(v,add);
+}
+
+void plottrack(v)
+vari *v;
+{ INT i, j;
+  plxyz *xyz;
+  varname vn;
+
+  cpl = get_graphics_window(v);
+
+  if ((cpl->ty & PLTRK)!=PLTRK) /* initialize */
+  { xyz = nextxyz(cpl,0,1);
+    xyz->x = createvar(pvarname(xyz,'x',vn),STPLOTVAR,100,VDOUBLE);
+    xyz->y = createvar(pvarname(xyz,'y',vn),STPLOTVAR,100,VDOUBLE);
+    xyz->z = createvar(pvarname(xyz,'z',vn),STPLOTVAR,100,VDOUBLE);
+    if (lf_error) return;
+    vlength(xyz->x) = vlength(xyz->y) = vlength(xyz->z) = 0;
+    settype(xyz,NULL,'p');
+    cpl->theta = cpl->phi = 0;
+    cpl->ty = PLTRK;
+  }
+  else
+  { vlength(cpl->xyzs) = 0;
+    xyz = nextxyz(cpl,1,1);
+  }
+  j = vlength(xyz->x);
+  i = getarg(v,"x",1);
+  if (i>0)
+  { vassn(xyz->x,j,darith(argval(v,i)));
+    strcpy(cpl->xlab,argval(v,i));
+    vlength(xyz->x) = j+1;
+  }
+  i = getarg(v,"y",1);
+  if (i>0)
+  { vassn(xyz->y,j,darith(argval(v,i)));
+    strcpy(cpl->ylab,argval(v,i));
+    vlength(xyz->y) = j+1;
+  }
+  i = getarg(v,"z",1);
+  if (i>0)
+  { vassn(xyz->z,j,darith(argval(v,i)));
+    strcpy(cpl->zlab,argval(v,i));
+    vlength(xyz->z) = j+1;
+  }
+  plotopt(v,0);
+}
+
+void setplot(v)
+vari *v;
+{ INT i, j, w;
+  carg *ct;
+  varname tname;
+  i = getarg(v,"win",1);
+  if (i==0)
+  { ERROR(("setplot: no win argument"));
+    return;
+  }
+  sscanf(argval(v,i),"%d",&w);
+  if ((w<0) | (w>=MAXWIN))
+  { ERROR(("setplot: invalid win %s",argval(v,i)));
+    return;
+  }
+  if (vlength(v)==2)
+  { deletevar(pl[w].track);
+    pl[w].track = NULL;
+    return;
+  }
+  sprintf(tname,"=tpc%d",w);
+  pl[w].track = createvar(tname,STSYSTEM,v->n-2,VARGL);
+  j = 0;
+  pl[w].ty = PLNONE; /* to ensure previous track is cleared */
+  for (i=1; i<vlength(v); i++)
+  { if (!argused(v,i))
+    { ct = (carg *)viptr(pl[w].track,j);
+      ct->arg = argarg(v,i);
+      ct->val = argval(v,i);
+      setused(v,i);
+      j++;
+    }
+  }
+  sprintf(tname,"=tps%d",w);
+  setvarname(curstr,tname);
+}
diff --git a/src/locfit/cmd.c b/src/locfit/cmd.c
new file mode 100644
index 0000000..cc52b50
--- /dev/null
+++ b/src/locfit/cmd.c
@@ -0,0 +1,801 @@
+/*
+ *   Copyright (c) 1996-2000 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include <unistd.h>
+#ifdef DOS
+#include <dos.h>
+#endif
+
+#include "local.h"
+
+#ifdef CVERSION
+
+#define MAXK 20
+
+FILE *ofile;
+
+device devps, devwin;
+design des;
+lfit lf;
+vari *aru;
+
+extern plots pl[];
+pplot pp;
+struct lfcol mycol[MAXCOLOR];
+char *lfhome;
+extern char filename[100];
+
+INT lf_error, lfcm[10];
+
+vari *curstr;
+void cmdint();
+void del_lines();
+
+/*
+ INDEX  data input and output functions
+ savefit:   f/end for user savefit.
+ readdata:  f/end for readdata
+ savedata:  f/end for savedata
+ recondat:  reconnect data to fit
+ */
+
+void savefit(v,mode)
+vari *v;
+char *mode;
+{ INT j, fp;
+    char *filename;
+    filename = getargval(v,"file",1);
+    if (filename==NULL)
+    { ERROR(("savefit: no filename"));
+        return;
+    }
+    j = getarg(v,"fp",1);
+    fp = (j>0) ? getlogic(v,j) : 0;
+    dosavefit(&lf,filename,mode,fp);
+    if (mode[0]=='r') endfit();
+}
+
+void readdata(v)
+vari *v;
+{ INT i, fp;
+    i = getarg(v,"data",1);
+    if (i==0) i = getarg(v,"file",1);
+    if (i==0) { ERROR(("readdata: no file name")); return; }
+    fp = getarg(v,"fp",1);
+    fp = (fp>0) ? getlogic(v,fp) : 0;
+    doreaddata(argval(v,i),fp);
+}
+
+void savedata(v)
+vari *v;
+{ INT fp;
+    if (argarg(v,0)==NULL) { ERROR(("savedata: no file name")); return; }
+    fp = getarg(v,"fp",0);
+    fp = (fp>0) ? getlogic(v,fp) : 0;
+    dosavedata(v,fp);
+}
+
+void recondat(xonly,n)
+INT xonly, *n;
+{ INT i;
+    *n = -1;
+    for (i=0; i<lf.mi[MDIM]; i++) dvari(&lf,i) = vdptr(findvar(lf.xname[i],1,n));
+    if (lf_error | xonly) return;
+    lf.y = vdptr(findvar(lf.yname,1,n));
+    lf.c = vdptr(findvar(lf.cname,1,n));
+    lf.w = vdptr(findvar(lf.wname,1,n));
+    lf.base=vdptr(findvar(lf.bname,1,n));
+}
+
+/*
+ INDEX  Call fitting functions e.t.c.
+ ckap():       compute SCB constants.
+ crband():     regression bandwidths
+ ckdeb():      kde bandwidths
+ */
+
+void ckap(v)
+vari *v;
+{ INT i, nd;
+    nd = 0;
+    if (v->n==1) /* compute for existing fit */
+    { if (nofit()) { ERROR(("ckap: no fit, no arguments")); }
+    else recondat(0,&lf.mi[MN]);
+    }
+    else      /* new fit specification */
+        fitoptions(&lf,v,0);
+    if (lf_error) return;
+    lf.nk = constants(&des,&lf,lf.kap);
+    if (lf_error) { lf.nk=0; return; }
+    printf("kappa0:");
+    for (i=0; i<lf.nk; i++) printf(" %8.5f",lf.kap[i]);
+    printf("\n");
+}
+
+void crband(v)
+vari *v;
+{ double h[4];
+    INT i, kk, meth[4], nm;
+    meth[0] = 1; meth[1] = 2; meth[2] = 3; meth[3] = 4;
+    nm = 4;
+    fitoptions(&lf,v,0);
+    lf.mi[MDEG0] = lf.mi[MDEG]; lf.mi[MDEG] = 4;
+    rband(&des,&lf,h,meth,&nm,&kk);
+    for (i=0; i<nm; i++)
+        printf("%8.5f ",h[i]);
+    printf("\n");
+    return;
+}
+
+void ckdeb(v)
+vari *v;
+{ INT i, mm[6], nm, n;
+    double *x, band[6], h0, h1;
+    char meth[6][5];
+    strcpy(meth[0],"AIC");  strcpy(meth[1],"LCV");
+    strcpy(meth[2],"LSCV"); strcpy(meth[3],"BCV");
+    strcpy(meth[4],"SJPI"); strcpy(meth[5],"GKK");
+    n = -1;
+    i = getarg(v,"x",1);
+    x = vdptr(findvar(argval(v,i),1,&n));
+    if (lf_error) return;
+    h0 = 0.02; h1 = 1.0;
+    i = getarg(v,"h0",1); if (i>0) h0 = darith(argval(v,i));
+    i = getarg(v,"h1",1); if (i>0) h1 = darith(argval(v,i));
+    
+    deschk(des,n,1);
+    mm[0]=1; mm[1]=2; mm[2]=3; mm[3]=4; mm[4]=5; mm[5]=6; nm=6;
+    kdeselect(band,x,des.ind,h0,h1,mm,nm,WGAUS,n);
+    for (i=0; i<nm; i++)
+        printf("%s: %8.6f ",meth[mm[i]-1],band[i]);
+    printf("\n");
+}
+
+/*
+ INDEX post-fitting functions
+ docrit():    compute c for scb's.
+ crit():      f/end to docrit();
+ backtr():    back transform theta in likelihood models.
+ predict():   interpolate the fit.
+ printdata(): print the current dataset.
+ printfit():  print the current fit.
+ summdata():  summarize current dataset.
+ summfit():   summarize current fit.
+ */
+
+double docrit(v)
+vari *v;
+{ double df, al;
+    INT i;
+    df = 0; al = 0.05;
+    i = getarg(v,"df",1); if (i>0) sscanf(argval(v,i),"%lf",&df);
+    i = getarg(v,"al",1); if (i>0) sscanf(argval(v,i),"%lf",&al);
+    return(critval(lf.kap,lf.nk,lf.mi[MDIM],al,10,2,df));
+}
+
+void crit(v)
+vari *v;
+{ vari *vr;
+    vr = createvar("crit",STHIDDEN,1,VDOUBLE);
+    if (lf_error) return;
+    vassn(vr,0,docrit(v));
+    saveresult(vr,argarg(v,0),STREGULAR);
+}
+
+double backtr(th,mi,nd)
+double th;
+INT *mi, nd;
+{ if (nd>0) return(th);
+    return(invlink(th,mi[MLINK]));
+}
+
+void predict(vc)
+vari *vc;
+{ 
+    double *data[MXDIM];
+    varname vn;
+    INT i, k, j, gr, n, z, mg[MXDIM];
+    memset(mg, 0, sizeof(mg));
+    dosavefit(&lf,getargval(vc,"fit",0),"rb",(INT)0);
+    if (nofit()) ERROR(("predict: no fit to interpolate\n"));
+    if (lf_error)  return;
+    
+    gr=0;
+    i = getarg(vc,"grid",0);
+    if (i>0) gr = getlogic(vc,i);
+    
+    i = getarg(vc,"where",0);
+    if (i>0) n = setpppoints(&pp,argval(vc,i),NULL,lf.fl);
+    else
+    { 
+        for (j=0; j<lf.mi[MDIM]; j++)
+        { i = getarg(vc,lf.xname[j],1);
+            if (i==0)
+            { 
+                ERROR(("predict: missing variables"));
+                return;
+            }
+            if (gr) n = 0;
+            sprintf(vn,"_pred%d",j);
+            pp.data[j] = varith(argval(vc,i),vn,STPLOTVAR);
+            if (lf_error) return;
+            if (gr) mg[j] = pp.data[j]->n;
+        }
+        n = pp.data[0]->n;
+        pp.gr = 1+gr;
+    }
+    
+    for (j=0; j<lf.mi[MDIM]; j++) data[j] = vdptr(pp.data[j]);
+    pp.d = lf.mi[MDIM];
+    
+    switch(pp.gr)
+    { case 1:
+            n = pp.data[0]->n;
+            break;
+        case 2:
+            n = 1;
+            for (i=0; i<lf.mi[MDIM]; i++)
+            {
+                mg[i] = pp.data[i]->n;
+                n *= mg[i];
+            }
+            break;
+        case 3:
+            n = lf.mi[MN];
+            break;
+        case 4:
+            n = lf.nv;
+            break;
+        default:
+            ERROR(("cpreplot where problem"));
+    }
+    
+    if (argarg(vc,0)==NULL)
+        pp.fit = createvar("predict",STHIDDEN,n,VDOUBLE);
+    else
+        pp.fit = createvar(argarg(vc,0),STREGULAR,n,VDOUBLE);
+    if (lf_error) return;
+    pp.se = NULL;
+    cpreplot(&pp,vc,'n');
+    if (lf_error) return;
+    for (j=0; j<n; j++)
+        if (vitem(pp.fit,j)!=NOSLN)
+            vassn(pp.fit,j,backtr(vitem(pp.fit,j),lf.mi,lf.nd));
+    if (argarg(vc,0)!=NULL) return;
+    for (j=0; j<n; j++)
+    { for (i=0; i<lf.mi[MDIM]; i++)
+    { z = j;
+        if (pp.gr==2)
+        { for (k=0; k<i; k++) z /= mg[k];
+            z = z%mg[i];
+        }
+        //printf("%10.6f ",data[i][z]);
+    }
+        if (vitem(pp.fit,j)==NOSLN) printf("   Not computed\n");
+        else printf("   %10.6f\n",vitem(pp.fit,j));
+    }
+    deletevar(pp.fit);
+}
+
+void printfit(v)
+vari *v;
+{ INT d, i = 0, j, k, cs, ck, nk, wh[MAXK];
+    double rs, alp, c = 0, fh;
+    cs = ck = nk = 0; rs = 1.0;
+    
+    dosavefit(&lf,getargval(v,"fit",i),"rb",(INT)0);
+    for (i=1; i<v->n; i++) if (!argused(v,i))
+    { if (argvalis(v,i,"x"))     { setused(v,i); wh[nk++]=1; }
+        if (argvalis(v,i,"fhat"))  { setused(v,i); wh[nk++]=2; }
+        if (argvalis(v,i,"coef"))  { setused(v,i); wh[nk++]=2; }
+        if (argvalis(v,i,"nlx"))   { setused(v,i); wh[nk++]=3; }
+        if (argvalis(v,i,"infl"))  { setused(v,i); wh[nk++]=4; }
+        if (argvalis(v,i,"se"))    { setused(v,i); wh[nk++]=5; cs=1; }
+        if (argvalis(v,i,"cband")) { setused(v,i); wh[nk++]=7; cs=ck=1; }
+        if (argvalis(v,i,"h"))     { setused(v,i); wh[nk++]=8; }
+        if (argvalis(v,i,"deg"))   { setused(v,i); wh[nk++]=9; }
+    }
+    if (nk==0) /* default: x and fhat */
+    { wh[nk++] = 1; wh[nk++] = 2;
+    }
+    d = lf.mi[MDIM];
+    alp = 0.95;
+    
+    if (cs) rs = sqrt(lf.dp[DRV]);
+    if (ck)
+    { c = critval(lf.kap,lf.nk,lf.mi[MDIM],1-alp,10,2,0.0);
+        printf("using c = %8.5f\n",c);
+    }
+    
+    for (i=0; i<lf.nv; i++) if (!lf.s[i])
+    { fh = lf.coef[i]+addparcomp(&lf,evpt(&lf,i),PCOEF);
+        for (j=0; j<nk; j++) switch(wh[j])
+        { case 1:
+                for (k=0; k<d; k++)
+                    printf("%8.5f ",evptx(&lf,i,k));
+                break;
+            case 2: printf(" %12.6f ",backtr(fh,lf.mi,0)); break;
+            case 3: printf(" %12.6f ",lf.nlx[i]); break;
+            case 4: printf(" %12.6f ",lf.t0[i]); break;
+            case 5: printf(" %12.6f ",rs*lf.nlx[i]); break;
+            case 7: printf(" (%12.6f,%12.6f) ",fh-c*rs*lf.nlx[i],fh+c*rs*lf.nlx[i]);
+                break;
+            case 8: printf(" %12.6f ",lf.h[i]); break;
+            case 9: printf(" %6.4f ",lf.deg[i]); break;
+            default: ERROR(("prfit: what??"));
+        }
+        printf("\n");
+    }
+}
+
+vari *knotsvar(name,n)
+varname *name;
+INT n;
+{ vari *v;
+    v = createvar("=knotv",STHIDDEN,n,VDOUBLE);
+    if (lf_error) return(NULL);
+    if (name!=NULL) v = saveresult(v,name,STREGULAR);
+    return(v);
+}
+
+void knots(v)
+vari *v;
+{ INT i, j, k, n;
+    vari *vr;
+    if (nofit()) { ERROR(("knots: no fit")); return; }
+    n = lf.nv; /* should delete pseudo vertices */
+    for (k=0; k<v->n; k++)
+    { vr = NULL;
+        for (j=0; j<lf.mi[MDIM]; j++)
+            if (argvalis(v,k,lf.xname[j]))
+            { vr = knotsvar(argarg(v,k),n);
+                for (i=0; i<n; i++) vassn(vr,i,evptx(&lf,i,j));
+                setused(v,k);
+            }
+        if (argvalis(v,k,"fit")|argvalis(v,k,"coef"))
+        { vr = knotsvar(argarg(v,k),n);
+            for (i=0; i<n; i++) vassn(vr,i,backtr(lf.coef[i],lf.mi,0));
+            setused(v,k);
+        }
+        if (argvalis(v,k,"h")|argvalis(v,k,"band"))
+        { vr = knotsvar(argarg(v,k),n);
+            for (i=0; i<lf.nv; i++) vassn(vr,i,lf.h[i]);
+            setused(v,k);
+        }
+        if (argvalis(v,k,"deg"))
+        { vr = knotsvar(argarg(v,k),n);
+            for (i=0; i<lf.nv; i++) vassn(vr,i,lf.deg[i]);
+            setused(v,k);
+        }
+        ((carg *)viptr(v,k))->result = vr;
+    }
+}
+
+void summfit(v)
+vari *v;
+{ int i;
+    dosavefit(&lf,getargval(v,"fit",1),"rb",0);
+    printf("Response variable: %s\n",lf.yname);
+    printf("Predictor variables: ");
+    for (i=0; i<lf.mi[MDIM]; i++) printf("%s ",lf.xname[i]);
+    printf("\nDegree of fit: %d\n",lf.mi[MDEG]);
+    printf("Smoothing parameters: NN %f  fix %f  pen %f\n",
+           lf.dp[DALP],lf.dp[DFXH],lf.dp[DADP]);
+    printf("Fitting Family: ");
+    switch(lf.mi[MTG]&63)
+    { case TDEN: printf("Density Estimation\n"); break;
+        case TRAT: printf("Poisson Process Rate Estimation\n"); break;
+        case THAZ: printf("Hazard Rate Estimation\n"); break;
+        case TGAUS:printf("Local Regression\n"); break;
+        case TLOGT:printf("Binomial\n"); break;
+        case TPOIS:printf("Poisson\n"); break;
+        case TGAMM:printf("Exponential/Gamma\n"); break;
+        case TGEOM:printf("Geometric/Negative Binomial\n"); break;
+        case TCIRC:printf("Circular - Von Mises\n"); break;
+    }
+    printf("Fitted Degrees of Freedom: %8.5f\n",lf.dp[DT0]);
+    printf("Number of fit points: %d\n",lf.nv);
+    printf("Evaluation structure: ");
+    switch(lf.mi[MEV])
+    { case ENULL: printf("None\n"); break;
+        case ETREE: printf("Rectangular tree\n"); break;
+        case EPHULL:printf("Triangulation\n"); break;
+        case EDATA: printf("Data\n"); break;
+        case EGRID: printf("Grid\n"); break;
+        case EKDTR: printf("K-d Tree\n"); break;
+        case EKDCE: printf("K-d Tree (centers)\n"); break;
+        case ECROS: printf("Data, Cross-Validation\n"); break;
+        case EPRES: printf("User-provided\n"); break;
+        default:    printf("Unknown\n");
+    }
+}
+
+void AC(name,r,g,b,p)
+char *name;
+INT r, g, b, p;
+{ devwin.AddColor(name,r,g,b,p);
+    devps.AddColor(name,r,g,b,p);
+}
+
+INT getcolidx(cname, def)
+char *cname;
+int def;
+{ int i;
+    if (cname==NULL) return(def);
+    for (i=0; i<8; i++)
+        if (strcmp(cname,mycol[i].name)==0) return(i);
+    WARN(("color %s not found",cname));
+    return(def);
+}
+
+void greyscale(v)
+vari *v;
+{ INT i, j0, j1;
+    j0 = getcolidx(getargval(v,"lo",1),0);
+    j1 = getcolidx(getargval(v,"hi",1),1);
+    for (i=0; i<=10; i++)
+        AC("",((10-i)*mycol[j0].r+i*mycol[j1].r)/11,
+           ((10-i)*mycol[j0].g+i*mycol[j1].g)/11,
+           ((10-i)*mycol[j0].b+i*mycol[j1].b)/11,8+i);
+}
+
+void setcolor(v)
+vari *v;
+{
+    return NULL;
+//   int i;
+//    lfcm[CBAK] = getcolidx(getargval(v,"back",0),lfcm[CBAK]);
+//    
+//    i = getarg(v,"fore",1);
+//    if (i>0)
+//    { lfcm[CAXI] = getcolidx(argval(v,i));
+//        for (i=CTEX; i<CPA2; i++) lfcm[i] = lfcm[CAXI];
+//    }
+//    
+//    lfcm[CAXI] = getcolidx(getargval(v,"axis",0),lfcm[CAXI]);
+//    lfcm[CTEX] = getcolidx(getargval(v,"text",0),lfcm[CTEX]);
+//    lfcm[CLIN] = getcolidx(getargval(v,"lines",0),lfcm[CLIN]);
+//    lfcm[CPOI] = getcolidx(getargval(v,"points",0),lfcm[CPOI]);
+//    lfcm[CCON] = getcolidx(getargval(v,"cont",0),lfcm[CCON]);
+//    lfcm[CCLA] = getcolidx(getargval(v,"clab",0),lfcm[CCLA]);
+//    lfcm[CSEG] = getcolidx(getargval(v,"cseg",0),lfcm[CSEG]);
+//    lfcm[CPA1] = getcolidx(getargval(v,"patch1",0),lfcm[CPA1]);
+//    lfcm[CPA2] = getcolidx(getargval(v,"patch2",0),lfcm[CPA2]);
+//    if (lfcm[CAXI]==lfcm[0]) WARN(("axis color = background color"));
+//    if (lfcm[CTEX]==lfcm[0]) WARN(("text color = background color"));
+//    if (lfcm[CLIN]==lfcm[0]) WARN(("lines color = background color"));
+//    if (lfcm[CPOI]==lfcm[0]) WARN(("points color = background color"));
+//    if (lfcm[CCON]==lfcm[0]) WARN(("cont color = background color"));
+//    if (lfcm[CCLA]==lfcm[0]) WARN(("clab color = background color"));
+//    if (lfcm[CSEG]==lfcm[0]) WARN(("cseg color = background color"));
+//    if (lfcm[CPA1]==lfcm[0]) WARN(("patch1 color = background color"));
+//    if (lfcm[CPA2]==lfcm[0]) WARN(("patch2 color = background color"));
+//    if (lfcm[CPA1]==lfcm[CPA2]) WARN(("patch1 color = patch2 color"));
+}
+
+void table(v)
+vari *v;
+{ INT i = 0, j = 0, ix, iy, m, mx, my, n, nx[15], ny[15], count[100];
+    double xl[2], yl[2], xs[15], ys[15], *x, *y;
+    i = getarg(v,"x",1);
+    if (i==0)
+    { ERROR(("table: no x variable"));
+        return;
+    }
+    n = -1;
+    x = vdptr(findvar(argval(v,i),1,&n));
+    xl[0] = xl[1] = x[0];
+    for (i=1; i<n; i++)
+    { if (x[i]<xl[0]) xl[0] = x[i];
+        if (x[i]>xl[1]) xl[1] = x[i];
+    }
+    i = getarg(v,"m",0);
+    if (i>0) sscanf(argval(v,i),"%d",&m); else m = 5;
+    mx = pretty(xl,m,xs);
+    if (lf_error) return;
+    
+    i = getarg(v,"y",1);
+    if (i>0)
+    { y = vdptr(findvar(argval(v,i),1,&n));
+        yl[0] = yl[1] = y[0];
+        for (i=1; i<n; i++)
+        { if (y[i]<yl[0]) yl[0] = y[i];
+            if (y[i]>yl[1]) yl[1] = y[i];
+        }
+        my = pretty(yl,m,ys);
+    }
+    else { y = NULL; my = 0; }
+    if (lf_error) return;
+    
+    for (i=0; i<15; i++) nx[i] = ny[i] = 0;
+    for (i=0; i<=(mx+1)*(my+1); i++) count[i] = 0;
+    for (i=0; i<n; i++)
+    { if (x[i]<xs[0]) ix = 0;
+        if (x[i]>=xs[mx-1]) ix = mx;
+        if ((x[i]>=xs[0]) & (x[i]<xs[mx-1]))
+            for (j=1; j<mx; j++)
+                if ((x[i]>=xs[j-1]) & (x[i]<xs[j])) ix = j;
+        if (my>0)
+        { if (y[i]<ys[0]) iy = 0;
+            if (y[i]>=ys[my-1]) iy = my;
+            if ((y[i]>=ys[0]) & (y[i]<ys[my-1]))
+                for (j=1; j<my; j++)
+                    if ((y[i]>=ys[j-1]) & (y[i]<ys[j])) iy = j;
+        } else iy = 0;
+        nx[ix] = ny[iy] = 1;
+        count[ix*(my+1)+iy]++;
+    }
+    if (my>0) printf("          ");
+    for (i=0; i<=mx; i++) if (nx[i]>0)
+        printf("  %4g-",(i==0) ? xl[0] : xs[i-1]);
+    printf("\n");
+    if (my>0) printf("          ");
+    for (i=0; i<=mx; i++) if (nx[i]>0)
+        printf("  %4g ",(i==mx) ? xl[1] : xs[i]);
+    printf("\n\n");
+    for (j=0; j<=my; j++) if (ny[j]>0)
+    { if (my>0)
+        printf("%4g-%4g ",(j==0) ? yl[0] : ys[j-1],
+               (j==my) ? yl[1] : ys[j]);
+        for (i=0; i<=mx; i++)
+            if (nx[i]>0) printf("%6d ",count[i*(my+1)+j]);
+        printf("\n");
+    }
+}
+
+/*
+ INDEX control functions:
+ setout(): set output file.
+ cmdint(): send off the command...
+ locfit_dispatch(): called by the main program.
+ */
+
+void setout(v)
+vari *v;
+{ INT i, i0;
+    char md;
+    i0 = getarg(v,"file",1);
+    if (i0==0)
+    { if (ofile!=NULL) fclose(ofile);
+        ofile = NULL;
+        printf("Output set to stdout\n");
+        return;
+    }
+    
+    md = 'w';
+    i = getarg(v,"mode",1);
+    if ((i>0) && (argval(v,i)[0]=='a')) md = 'a';
+    
+    setfilename(argval(v,i0),"",&md,0);
+    if (ofile != NULL) fclose(ofile);
+    ofile = fopen(filename,&md);
+    if (ofile == NULL)
+        ERROR(("setout: can't open %s for writing",filename));
+    else
+        printf("Output set to file %s\n",filename);
+}
+
+void dosleep(v)
+vari *v;
+{ INT i;
+    i = getarg(v,"time",1);
+    if (i==0) return;
+    sscanf(argval(v,i),"%d",&i);
+    (void)sleep(i);
+}
+
+void setdef(v)
+vari *v;
+{ INT i, n;
+    carg *ca;
+    vari *vd;
+    
+    if (argarg(v,0)==NULL)
+    { ERROR(("Unnamed Defintion"));
+        return;
+    }
+    n = vlength(v)-1;
+    vd = createvar(argarg(v,0),STSYSTEM,n,VARGL);
+    if (lf_error) return;
+    
+    for (i=0; i<n; i++)
+    { ca = (carg *)viptr(vd,i);
+        ca->arg = argarg(v,i+1);
+        ca->val = argval(v,i+1);
+        setused(v,i+1);
+    }
+    sprintf(curstr->name,"=%s",argarg(v,0));
+}
+
+extern void cscbsim();
+
+void dcmdint(v)
+vari *v;
+{ INT i;
+    if (v==NULL)
+    { ERROR(("dcmdint received NULL"));
+        return;
+    }
+    if (argvalis(v,0,"band"))  { band(v); return; }
+    if (argvalis(v,0,"crit"))  { crit(v); return; }
+    if (argvalis(v,0,"def"))   { setdef(v); return; }
+    if (argvalis(v,0,"endfor")) { dec_forvar(); return; }
+    if (argvalis(v,0,"for"))    { inc_forvar(); return; }
+    if (argvalis(v,0,"example")){example(v); return; }
+    if (argvalis(v,0,"help"))   {example(v); return; }
+    if (argvalis(v,0,"?"))      {example(v); return; }
+    if (argvalis(v,0,"exit")) exit(0);
+    if (argvalis(v,0,"quit")) exit(0);
+    if (argvalis(v,0,"q()"))  exit(0);
+    if (argvalis(v,0,"fitted")){ cfitted(v,RMEAN); return; }
+    if (argvalis(v,0,"greyscale"))   { greyscale(v); return; }
+    if (argvalis(v,0,"kappa")) { ckap(v); return; }
+    if (argvalis(v,0,"kdeb"))  { ckdeb(v); return; }
+    if (argvalis(v,0,"knots")) { knots(v); return; }
+    if (argvalis(v,0,"locfit"))   { clocfit(v,0); return; }
+    if (argvalis(v,0,"relocfit")) { clocfit(v,1); return; }
+    if (argvalis(v,0,"plot"))     { printf("use plotfit or plotdata\n"); return; }
+    if (argvalis(v,0,"plotdata")) { plotdata(v); return; }
+    if (argvalis(v,0,"plotfit"))  { plotfit(v); return; }
+    if (argvalis(v,0,"replot"))   { plotopt(v,1); return; }
+    if (argvalis(v,0,"predict"))  { predict(v); return; }
+    if (argvalis(v,0,"prfit"))    { printfit(v); return; }
+    if (argvalis(v,0,"rband"))    { crband(v); return; }
+    if (argvalis(v,0,"readdata")) { readdata(v); return; }
+    if (argvalis(v,0,"readfile")) { readfile(v); return; }
+    if (argvalis(v,0,"readfit"))  { savefit(v,"rb"); return; }
+    if (argvalis(v,0,"residuals")){ cfitted(v,RDEV); return; }
+    if (argvalis(v,0,"run"))      return;
+    if (argvalis(v,0,"savedata")) { savedata(v); return; }
+    if (argvalis(v,0,"savefit"))  { savefit(v,"wb"); return; }
+    if (argvalis(v,0,"scbmax"))   { cscbsim(v); return; }
+    if (argvalis(v,0,"scbsim"))   { cscbsim(v); return; }
+    if (argvalis(v,0,"seed"))     { rseed(argval(v,1)); setused(v,1); return; }
+    if (argvalis(v,0,"setcolor")) { setcolor(v); return; }
+    if (argvalis(v,0,"setout"))   { setout(v); return; }
+    if (argvalis(v,0,"outf"))     { setout(v); return; }
+    if (argvalis(v,0,"setplot"))  { setplot(v); return; }
+    if (argvalis(v,0,"sleep"))    { dosleep(v); return; }
+    if (argvalis(v,0,"summfit"))  { summfit(v); return; }
+    if (argvalis(v,0,"table"))    { table(v); return; }
+    if (argvalis(v,0,"track"))    { plottrack(v); return; }
+    if (argvalis(v,0,"wdiag"))    { cwdiag(v); return; }
+    for (i=0; i<vlength(v); i++)
+    { ((carg *)viptr(v,i))->result = varith(argval(v,i),argarg(v,i),STREGULAR);
+        setused(v,i);
+        if (lf_error) return;
+    }
+}
+
+void cmdint(v)
+vari *v;
+{ vari *vv, *vr;
+    INT i, j, mn, nr;
+    if (v==NULL) return;
+    
+    for (i=0; i<vlength(v); i++)
+    { setunused(v,i);
+        /* ((carg *)viptr(v,i))->used = 0; */
+        ((carg *)viptr(v,i))->result = NULL;
+    }
+    
+    setused(v,0);
+    if (vlength(v)==1)
+    { j = 0;
+        vv = findvar(argval(v,0),0,&j);
+        if ((vv!=NULL) && ((vv->mode==VARGL) & (!argvalis(v,0,"=cline"))))
+        { 
+            cmdint(vv);
+            return;
+        }
+    }
+    
+    /* dcmdint processes command */
+    dcmdint(v);
+    
+    /* print the results of unassigned expression.
+     * First, determine mn = maximum number of rows in the
+     * output. Note that vr->stat==STHIDDEN determines whether
+     * the result was unassigned.
+     */
+    mn = 0; nr = 0;
+    for (i=0; i<vlength(v); i++)
+    { vr = ((carg *)viptr(v,i))->result;
+        if ((vr != NULL) && (vr->stat==STHIDDEN))
+            switch(vr->mode)
+        { case VCHAR: if (mn<1) mn = 1;
+                break;
+            case VINT:
+            case VDOUBLE: if (mn<vr->n) mn = vr->n;
+                break;
+        }
+    }
+    
+    /* now, print the unassigned variables.
+     
+     for (i=0; i<mn; i++)
+     { for (j=0; j<vlength(v); j++)
+     { vr = ((carg *)viptr(v,j))->result;
+     if ((vr != NULL) && (vr->stat==STHIDDEN))
+     switch(vr->mode)
+     { case VDOUBLE: printf("%8.5f  ",vitem(vr,i)); break;
+     case VCHAR:   printf("%s  ",vdptr(vr)); break;
+     case VINT:    printf("%4d  ", vitem(vr,i)); break;
+     }
+     }
+     printf("\n");
+     }
+     */
+    
+    for (i=0; i<vlength(v); i++)
+        deleteifhidden(((carg *)viptr(v,i))->result);
+}
+
+INT locfit_dispatch(char *z)
+
+{ vari *v;
+    
+    makecmd(z);
+    while (1)
+    { lf_error = 0;
+        v = getcmd();
+        if (v==NULL)
+        { del_lines();
+            return(0);
+        }
+        cmdint(v);
+    }
+}
+
+void setuplf()
+{ INT i;
+    char command[100];
+    vari *v;
+    
+    lfhome = getenv("LFHOME");
+    initdb();
+    
+    ofile = NULL;
+    lf.tw = lf.xxev = lf.L = lf.iw = des.dw = lf.pc.wk = NULL;
+    des.index = NULL;
+    lf.mg = calloc(MXDIM,sizeof(INT));
+    
+    v = createvar("mi",STSYSPEC,LENM,VINT); v->dpr = (double *)lf.mi;
+    v = createvar("dp",STSYSPEC,LEND,VDOUBLE); v->dpr = lf.dp;
+    v = createvar("alpha",STSYSPEC,1,VDOUBLE); v->dpr = &lf.dp[DALP];
+    v = createvar("h",    STSYSPEC,1,VDOUBLE); v->dpr = &lf.dp[DFXH];
+    v = createvar("pen",  STSYSPEC,1,VDOUBLE); v->dpr = &lf.dp[DADP];
+    v = createvar("infl", STSYSPEC,1,VDOUBLE); v->dpr = &lf.dp[DT0];
+    v = createvar("vari", STSYSPEC,1,VDOUBLE); v->dpr = &lf.dp[DT1];
+    v = createvar("like", STSYSPEC,1,VDOUBLE); v->dpr = &lf.dp[DLK];
+    v = createvar("resv", STSYSPEC,1,VDOUBLE); v->dpr = &lf.dp[DRV];
+    
+    for (i=0; i<MAXWIN; i++)
+    { pl[i].xyzs = NULL;
+        pl[i].id = i;
+        pl[i].ty = PLNONE;
+        pl[i].track = NULL;
+    }
+    //SetWinDev(&devwin);
+    //SetPSDev(&devps);
+    //  AC("white",255,255,255,0);
+    //  AC("black",  0,  0,  0,1);
+    //  AC(  "red",255,  0,  0,2);
+    //  AC("green",  0,255,  0,3);
+    //  AC( "blue",  0,  0,255,4);
+    //  AC("magenta",255,0,255,5);
+    //  AC("yellow",255,255, 0,6);
+    //  AC( "cyan",  0,255,255,7);
+    lfcm[0] = 0;
+    for (i=CAXI; i<=CPA1; i++) lfcm[i] = 1;
+    lfcm[CPA2] = 2;
+    rseed("LocalFit");
+    if (setfilename("LFInit","cmd","r",0))
+    { sprintf(command,"run %s",filename);
+        locfit_dispatch(command);
+    }
+}
+
+#endif
diff --git a/src/locfit/dens_haz.c b/src/locfit/dens_haz.c
new file mode 100644
index 0000000..a3159c7
--- /dev/null
+++ b/src/locfit/dens_haz.c
@@ -0,0 +1,190 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *
+ *   Integration for hazard rate estimation. The functions in this
+ *   file are used to evaluate
+ *      sum int_0^{Ti} W_i(t,x) A()A()' exp( P() ) dt
+ *   for hazard rate models.
+ *
+ *   These routines assume the weight function is supported on [-1,1].
+ *   hasint_sph multiplies by exp(base(lf,i)), which allows estimating
+ *   the baseline in a proportional hazards model, when the covariate
+ *   effect base(lf,i) is known.
+ *
+ *   TODO:
+ *     hazint_sph, should be able to reduce mint in some cases with
+ *       small integration range. onedint could be used for beta-family
+ *       (RECT,EPAN,BISQ,TRWT) kernels.
+ *     hazint_prod, restrict terms from the sum based on x values.
+ *       I should count obs >= max, and only do that integration once.
+ */
+
+#include "local.h"
+
+static double ilim[2*MXDIM], *ff, tmax;
+
+/*
+ *  hrao returns 0 if integration region is empty.
+ *               1 otherwise.
+ */
+INT haz_sph_int(lf,dfx,cf,h,r1)
+lfit *lf;
+double *dfx, *cf, h, *r1;
+{ double s, t0, t1, wt, th;
+  INT dim, j, p, *mi;
+  mi = lf->mi;
+  s = 0; p = mi[MP];
+  dim = mi[MDIM];
+  for (j=1; j<dim; j++) s += SQR(dfx[j]/(h*lf->sca[j]));
+  if (s>1) return(0);
+
+  setzero(r1,p*p);
+  t1 = sqrt(1-s)*h*lf->sca[0];
+  t0 = -t1;
+  if (t0<ilim[0])   t0 = ilim[0];
+  if (t1>ilim[dim]) t1 = ilim[dim];
+  if (t1>dfx[0]) t1 = dfx[0];
+  if (t1<t0) return(0);
+
+/*  Numerical integration by Simpson's rule.
+ */
+  for (j=0; j<=mi[MMINT]; j++)
+  { dfx[0] = t0+(t1-t0)*j/mi[MMINT];
+    wt = weight(lf,dfx,NULL,h,0,0.0);
+    fitfun(lf,dfx,NULL,ff,NULL,0);
+    th = innerprod(cf,ff,p);
+    if (mi[MLINK]==LLOG) th = exp(th);
+    wt *= 2+2*(j&1)-(j==0)-(j==mi[MMINT]);
+    addouter(r1,ff,ff,p,wt*th);
+  }
+  multmatscal(r1,(t1-t0)/(3*mi[MMINT]),p*p);
+
+  return(1);
+}
+
+INT hazint_sph(t,resp,r1,lf,cf,h)
+lfit *lf;
+double *t, *resp, *r1, *cf, h;
+{ INT i, j, p, st;
+  double dfx[MXDIM], eb, sb;
+  p = lf->mi[MP];
+  setzero(resp,p*p);
+  sb = 0.0;
+
+  for (i=0; i<=lf->mi[MN]; i++)
+  {
+    if (i==lf->mi[MN])
+    { dfx[0] = tmax-t[0];
+      for (j=1; j<lf->mi[MDIM]; j++) dfx[j] = 0.0;
+      eb = exp(sb/lf->mi[MN]);
+    }
+    else
+    { eb = exp(base(lf,i)); sb += base(lf,i);
+      for (j=0; j<lf->mi[MDIM]; j++) dfx[j] = datum(lf,j,i)-t[j];
+    }
+
+    st = haz_sph_int(lf,dfx,cf,h,r1);
+    if (st)
+      for (j=0; j<p*p; j++) resp[j] += eb*r1[j];
+  }
+  return(LF_OK);
+}
+
+INT hazint_prod(t,resp,x,lf,cf,h)
+lfit *lf;
+double *t, *resp, *x, *cf, h;
+{ INT d, p, deg, i, j, k, st;
+  double dfx[MXDIM], t_prev,
+         hj, hs, ncf[MXDEG], ef, il1;
+  double prod_wk[MXDIM][2*MXDEG+1], eb, sb;
+
+  p = lf->mi[MP]; d = lf->mi[MDIM];
+  deg = lf->mi[MDEG];
+  setzero(resp,p*p);
+  hj = hs = h*lf->sca[0];
+    memset(dfx, 0.0, sizeof(dfx));
+  ncf[0] = cf[0];
+  for (i=1; i<=deg; i++)
+  { ncf[i] = hj*cf[(i-1)*d+1]; hj *= hs;
+  }
+
+/*   for i=0..n....
+ *     First we compute prod_wk[j], j=0..d.
+ *     For j=0, this is int_0^T_i (u-t)^k W((u-t)/h) exp(b0*(u-t)) du
+ *     For remaining j,   (x(i,j)-x(j))^k Wj exp(bj*(x..-x.))
+ *
+ *     Second, we add to the integration (exp(a) incl. in integral)
+ *     with the right factorial denominators.
+ */
+  t_prev = ilim[0]; sb = 0.0;
+  for (i=0; i<=lf->mi[MN]; i++)
+  { if (i==lf->mi[MN])
+    { dfx[0] = tmax-t[0];
+      for (j=1; j<d; j++) dfx[j] = 0.0;
+      eb = exp(sb/lf->mi[MN]);
+    }
+    else
+    { eb = exp(base(lf,i)); sb += base(lf,i);
+      for (j=0; j<d; j++) dfx[j] = datum(lf,j,i)-t[j];
+    }
+
+    if (dfx[0]>ilim[0]) /* else it doesn't contribute */
+    {
+/* time integral */
+      il1 = (dfx[0]>ilim[d]) ? ilim[d] : dfx[0];
+      if (il1 != t_prev) /* don't repeat! */
+      { st = onedint(ncf,lf->mi,ilim[0]/hs,il1/hs,prod_wk[0]);
+        if (st>0) return(st);
+        hj = eb;
+        for (j=0; j<=2*deg; j++)
+        { hj *= hs;
+          prod_wk[0][j] *= hj;
+        }
+        t_prev = il1;
+      }
+
+/* covariate terms */
+      for (j=1; j<d; j++)
+      {
+        ef = 0.0;
+        for (k=deg; k>0; k--) ef = (ef+dfx[j])*cf[1+(k-1)*d+j];
+        ef = exp(ef);
+        prod_wk[j][0] = ef * W(dfx[j]/(h*lf->sca[j]),lf->mi[MKER]);
+        for (k=1; k<=2*deg; k++)
+          prod_wk[j][k] = prod_wk[j][k-1] * dfx[j];
+      }
+
+/*  add to the integration.  */
+      prodint_resp(resp,prod_wk,d,deg,p);
+    } /* if dfx0 > ilim0 */
+  } /* n loop */
+
+/* symmetrize */
+  for (k=0; k<p; k++)
+    for (j=k; j<p; j++)
+      resp[j*p+k] = resp[k*p+j];
+  return(LF_OK);
+}
+
+INT hazint(t,resp,resp1,lf,cf,h)
+lfit *lf;
+double *t, *resp, *resp1, *cf, h;
+{ if (lf->mi[MDIM]==1) return(hazint_prod(t,resp,resp1,lf,cf,h));
+  if (lf->mi[MKT]==KPROD) return(hazint_prod(t,resp,resp1,lf,cf,h));
+
+  return(hazint_sph(t,resp,resp1,lf,cf,h));
+}
+
+void haz_init(lf,des,il)
+lfit *lf;
+design *des;
+double *il;
+{ int i;
+  tmax = datum(lf,0,0);
+  for (i=1; i<lf->mi[MN]; i++) tmax = MAX(tmax,datum(lf,0,i));
+  ff = des->xtwx.wk;
+  for (i=0; i<2*lf->mi[MDIM]; i++) ilim[i] = il[i];
+}
diff --git a/src/locfit/dens_int.c b/src/locfit/dens_int.c
new file mode 100644
index 0000000..eda5df3
--- /dev/null
+++ b/src/locfit/dens_int.c
@@ -0,0 +1,223 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *   The function dens_integrate(lf,des,z) is used to integrate a density
+ *   estimate (z=1) or the density squared (z=2). This is used to renormalize
+ *   the estimate (function dens_renorm) or in the computation of LSCV
+ *   (function dnes_lscv). The implementation is presently for d=1.
+ *
+ *   The computation orders the fit points selected by locfit, and
+ *   integrates analytically over each interval. For the log-link,
+ *   the interpolant used is peicewise quadratic (with one knot in
+ *   the middle of each interval); this differs from the cubic interpolant
+ *   used elsewhere in Locfit.
+ *
+ *   TODO: allow for xlim. What can be done simply in >=2 dimensions?
+ *         fix df computation (in lscv) for link=IDENT.
+ */
+
+#include "local.h"
+
+/*
+ * Finds the order of observations in the array x, and
+ * stores in integer array ind.
+ * At input, lset l=0 and r=length(x)-1.
+ * At output, x[ind[0]] <= x[ind[1]] <= ...
+ */
+void lforder(ind,x,l,r)
+INT *ind, l, r;
+double *x;
+{ double piv;
+  INT i, i0, i1;
+  piv = (x[ind[l]]+x[ind[r]])/2;
+  i0 = l; i1 = r;
+  while (i0<=i1)
+  { while ((i0<=i1) && (x[ind[i0]]<=piv)) i0++;
+    while ((i0<=i1) && (x[ind[i1]]>piv))  i1--;
+    if (i0<i1)
+    { ISWAP(ind[i0],ind[i1]);
+      i0++; i1--;
+    }
+  }
+  /* now, x[ind[l..i1]] <= piv < x[ind[i0..r]].
+     put the ties in the middle */
+  while ((i1>=l) && (x[ind[i1]]==piv)) i1--;
+  for (i=l; i<=i1; i++)
+    if (x[ind[i]]==piv)
+    { ISWAP(ind[i],ind[i1]);
+      while (x[ind[i1]]==piv) i1--;
+    }
+
+  if (l<i1) lforder(ind,x,l,i1);
+  if (i0<r) lforder(ind,x,i0,r);
+}
+
+/*
+ *  estdiv integrates the density between fit points x0 and x1.
+ *  f0, f1 are function values, d0, d1 are derivatives.
+ */
+double estdiv(x0,x1,f0,f1,d0,d1,link)
+double x0, x1, f0, f1, d0, d1;
+INT link;
+{ double cf[4], I[2], dlt, e0, e1;
+
+  if (x0==x1) return(0.0);
+
+  if (link==LIDENT)
+  {
+/* cf are integrals of hermite polynomials.
+ * Then adjust for x1-x0.
+ */
+    cf[0] = cf[1] = 0.5;
+    cf[2] = 1.0/12.0; cf[3] = -cf[2];
+    return( (cf[0]*f0+cf[1]*f1)*(x1-x0)
+          + (cf[2]*d0+cf[3]*d1)*(x1-x0)*(x1-x0) );
+  }
+
+/*
+ * this is for LLOG
+ */
+
+  dlt = (x1-x0)/2;
+  cf[0] = f0;
+  cf[1] = d0;
+  cf[2] = ( 2*(f1-f0) - dlt*(d1+3*d0) )/(4*dlt*dlt);
+  recurint(0.0,dlt,cf,I,0,WRECT);
+  e0 = I[0];
+
+  cf[0] = f1;
+  cf[1] = -d1;
+  cf[2] = ( 2*(f0-f1) + dlt*(d0+3*d1) )/( 4*dlt*dlt );
+  recurint(0.0,dlt,cf,I,0,WRECT);
+  e1 = I[0];
+
+  return(e0+e1);
+}
+
+/*
+ *   Evaluate the integral of the density estimate to the power z.
+ *   This would be severely messed up, if I ever implement parcomp
+ *   for densities.
+ */
+double dens_integrate(lf,des,z)
+lfit *lf;
+design *des;
+INT z;
+{ INT has_deriv, i, i0, i1, link, nv, *ind;
+  double *xev, *fit, *deriv, sum, term;
+  double d0, d1, f0, f1;
+
+  if (lf->mi[MDIM]>=2)
+  { WARN(("dens_integrate requires d=1"));
+    return(0.0);
+  }
+
+  link = lf->mi[MLINK];
+  has_deriv = (lf->mi[MDEG] > 0); /* not right? */
+  fit = lf->coef;
+  if (has_deriv)
+    deriv = &lf->coef[lf->nvm];
+  xev = vdptr(lf->xxev);
+
+  /*
+   * order the vertices
+   */
+  nv = lf->nv;
+  if (lf->mi[MN]<nv) return(0.0);
+  ind = des->ind;
+  for (i=0; i<nv; i++) ind[i] = i;
+  lforder(ind,xev,0,nv-1);
+  sum = 0.0;
+
+  /*
+   * Estimate the contribution of the boundaries.
+   * should really check flim here.
+   */
+  i0 = ind[0]; i1 = ind[1];
+  f1 = fit[i0];
+  d1 = (has_deriv) ? deriv[i0] :
+         (fit[i1]-fit[i0])/(xev[i1]-xev[i0]);
+  if (d1 <= 0) WARN(("dens_integrate - ouch!"));
+  if (z==2)
+  { if (link==LLOG)
+    { f1 *= 2; d1 *= 2; }
+    else
+    { d1 = 2*d1*f1; f1 = f1*f1; }
+  }
+  term = (link==LIDENT) ? f1*f1/(2*d1) : exp(f1)/d1;
+  sum += term;
+
+  i0 = ind[nv-2]; i1 = ind[nv-1];
+  f0 = fit[i1];
+  d0 = (has_deriv) ? deriv[i1] :
+         (fit[i1]-fit[i0])/(xev[i1]-xev[i0]);
+  if (d0 >= 0) WARN(("dens_integrate - ouch!"));
+  if (z==2)
+  { if (link==LLOG)
+    { f0 *= 2; d0 *= 2; }
+    else
+    { d0 = 2*d0*f0; f0 = f0*f0; }
+  }
+  term = (link==LIDENT) ? -f0*f0/(2*d0) : exp(f0)/d0;
+  sum += term;
+  
+  for (i=1; i<nv; i++)
+  { i0 = ind[i-1]; i1 = ind[i];
+    f0 = fit[i0]; f1 = fit[i1];
+    d0 = (has_deriv) ? deriv[i0] :
+              (f1-f0)/(xev[i1]-xev[i0]);
+    d1 = (has_deriv) ? deriv[i1] : d0;
+    if (z==2)
+    { if (link==LLOG)
+      { f0 *= 2; f1 *= 2; d0 *= 2; d1 *= 2; }
+      else
+      { d0 *= 2*f0; d1 *= 2*f1; f0 = f0*f0; f1 = f1*f1; }
+    }
+    term = estdiv(xev[i0],xev[i1],f0,f1,d0,d1,link);
+    sum += term;
+  }
+
+  return(sum);
+}
+
+void dens_renorm(lf,des)
+lfit *lf;
+design *des;
+{ INT i;
+  double sum;
+  sum = dens_integrate(lf,des,1);
+  if (sum==0.0) return;
+  sum = log(sum);
+  for (i=0; i<lf->nv; i++) lf->coef[i] -= sum;
+}
+
+void dens_lscv(des,lf)
+lfit *lf;
+design *des;
+{ double df, fh, fh_cv, infl, z0, z1, x[MXDIM];
+  int i, n, j, ev;
+  z1 = df = 0.0;
+  ev = lf->mi[MEV];
+  n = lf->mi[MN];
+  if ((ev==EDATA) | (ev==ECROS)) ev = EFITP;
+
+  z0 = dens_integrate(lf,des,2);
+
+  for (i=0; i<n; i++)
+  { for (j=0; j<lf->mi[MDIM]; j++) x[j] = datum(lf,j,i);
+    fh = base(lf,i)+dointpoint(lf,des,x,PCOEF,ev,i);
+    if (lf->mi[MLINK]==LLOG) fh = exp(fh);
+    infl = dointpoint(lf,des,x,PT0,ev,i);
+    infl = infl * infl;
+    if (infl>1) infl = 1;
+    fh_cv = (lf->mi[MLINK] == LIDENT) ?
+       (n*fh - infl) / (n-1.0) : fh*(1-infl)*n/(n-1.0);
+    z1 += fh_cv;
+    df += infl;
+  }
+
+  vdptr(lf->L)[0] = z0-2*z1/n;
+  vdptr(lf->L)[1] = df;
+}
diff --git a/src/locfit/dens_odi.c b/src/locfit/dens_odi.c
new file mode 100644
index 0000000..e7724ec
--- /dev/null
+++ b/src/locfit/dens_odi.c
@@ -0,0 +1,515 @@
+/*
+ *   Copyright (c) 1996-200 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *
+ *  Routines for one-dimensional numerical integration
+ *  in density estimation. The entry point is
+ *
+ *  onedint(cf,mi,l0,l1,resp)
+ *
+ *  which evaluates int W(u)u^j exp( P(u) ), j=0..2*deg.
+ *  P(u) = cf[0] + cf[1]u + cf[2]u^2/2 + ... + cf[deg]u^deg/deg!
+ *  l0 and l1 are the integration limits.
+ *  The results are returned through the vector resp.
+ *
+ */
+
+#include "local.h"
+
+static int debug;
+
+INT exbctay(b,c,n,z) /* n-term taylor series of e^(bx+cx^2) */
+double b, c, *z;
+INT n;
+{ double ec[20];
+  INT i, j;
+  z[0] = 1;
+  for (i=1; i<=n; i++) z[i] = z[i-1]*b/i;
+  if (c==0.0) return(n);
+  if (n>=40)
+  { WARN(("exbctay limit to n<40"));
+    n = 39;
+  }
+  ec[0] = 1;
+  for (i=1; 2*i<=n; i++) ec[i] = ec[i-1]*c/i;
+  for (i=n; i>1; i--)
+    for (j=1; 2*j<=i; j++)
+      z[i] += ec[j]*z[i-2*j];
+  return(n);
+}
+
+double explinjtay(l0,l1,j,cf)
+/* int_l0^l1 x^j e^(a+bx+cx^2); exbctay aroud l1 */
+double l0, l1, *cf;
+INT j;
+{ double tc[40], f, s;
+  INT k, n;
+  if ((l0!=0.0) | (l1!=1.0)) WARN(("explinjtay: invalid l0, l1"));
+  n = exbctay(cf[1]+2*cf[2]*l1,cf[2],20,tc);
+  s = tc[0]/(j+1);
+  f = 1/(j+1);
+  for (k=1; k<=n; k++)
+  { f *= -k/(j+k+1.0);
+    s += tc[k]*f;
+  }
+  return(f);
+}
+
+void explint1(l0,l1,cf,I,p) /* int x^j exp(a+bx); j=0..p-1 */
+double l0, l1, *cf, *I;
+INT p;
+{ double y0, y1, f;
+  INT j, k, k1;
+  y0 = lf_exp(cf[0]+l0*cf[1]);
+  y1 = lf_exp(cf[0]+l1*cf[1]);
+  if (p<2*fabs(cf[1])) k = p; else k = (INT)fabs(cf[1]);
+
+  if (k>0)
+  { I[0] = (y1-y0)/cf[1];
+    for (j=1; j<k; j++) /* forward steps for small j */
+    { y1 *= l1; y0 *= l0;
+      I[j] = (y1-y0-j*I[j-1])/cf[1];
+    }
+    if (k==p) return;
+    y1 *= l1; y0 *= l0;
+  }
+
+  f = 1; k1 = k;
+  while ((k<50) && (f>1.0e-8)) /* initially Ik = diff(x^{k+1}e^{a+bx}) */
+  { y1 *= l1; y0 *= l0;
+    I[k] = y1-y0;
+    if (k>=p) f *= fabs(cf[1])/(k+1);
+    k++;
+  }
+  if (k==50) WARN(("explint1: want k>50"));
+  I[k] = 0.0;
+  for (j=k-1; j>=k1; j--) /* now do back step recursion */
+    I[j] = (I[j]-cf[1]*I[j+1])/(j+1);
+}
+
+void explintyl(l0,l1,cf,I,p) /* small c, use taylor series and explint1 */
+double l0, l1, *cf, *I;
+INT p;
+{ INT i;
+  double c;
+  explint1(l0,l1,cf,I,p+8);
+  c = cf[2];
+  for (i=0; i<p; i++)
+    I[i] = (((I[i+8]*c/4+I[i+6])*c/3+I[i+4])*c/2+I[i+2])*c+I[i];
+}
+
+void solvetrid(X,y,m)
+double *X, *y;
+INT m;
+{ INT i;
+  double s;
+  for (i=1; i<m; i++)
+  { s = X[3*i]/X[3*i-2];
+    X[3*i] = 0; X[3*i+1] -= s*X[3*i-1];
+    y[i] -= s*y[i-1];
+  }
+  for (i=m-2; i>=0; i--)
+  { s = X[3*i+2]/X[3*i+4];
+    X[3*i+2] = 0;
+    y[i] -= s*y[i+1];
+  }
+  for (i=0; i<m; i++) y[i] /= X[3*i+1];
+}
+
+void initi0i1(I,cf,y0,y1,l0,l1)
+double *I, *cf, y0, y1, l0, l1;
+{ double a0, a1, c, d, bi;
+  d = -cf[1]/(2*cf[2]); c = sqrt(2*fabs(cf[2]));
+  a0 = c*(l0-d); a1 = c*(l1-d);
+  if (cf[2]<0)
+  { bi = lf_exp(cf[0]+cf[1]*d+cf[2]*d*d)/c;
+    if (a0>0)
+    { if (a0>6) I[0] = (y0*ptail(-a0)-y1*ptail(-a1))/c;
+      else I[0] = S2PI*(pnorm(-a0,0.0,1.0)-pnorm(-a1,0.0,1.0))*bi;
+    }
+    else
+    { if (a1< -6) I[0] = (y1*ptail(a1)-y0*ptail(a0))/c;
+      else I[0] = S2PI*(pnorm(a1,0.0,1.0)-pnorm(a0,0.0,1.0))*bi;
+    }
+  }
+  else
+    I[0] = (y1*daws(a1)-y0*daws(a0))/c;
+  I[1] = (y1-y0)/(2*cf[2])+d*I[0];
+}
+
+void explinsid(l0,l1,cf,I,p) /* large b; don't use fwd recursion */
+double l0, l1, *cf, *I;
+INT p;
+{ INT k, k0, k1, k2;
+  double y0, y1, Z[150];
+if (debug) printf("side: %8.5f %8.5f %8.5f    limt %8.5f %8.5f  p %2d\n",cf[0],cf[1],cf[2],l0,l1,p);
+ 
+  k0 = 2;
+  k1 = (INT)(fabs(cf[1])+fabs(2*cf[2]));
+  if (k1<2) k1 = 2;
+  if (k1>p+20) k1 = p+20;
+  k2 = p+20;
+
+  if (debug) printf("k0 %2d  k1 %2d  k2 %2d  p %2d\n",k0,k1,k2,p);
+
+  y0 = lf_exp(cf[0]+l0*(cf[1]+l0*cf[2]));
+  y1 = lf_exp(cf[0]+l1*(cf[1]+l1*cf[2]));
+  initi0i1(I,cf,y0,y1,l0,l1);
+if (debug) printf("i0 %8.5f  i1 %8.5f\n",I[0],I[1]);
+
+  y1 *= l1; y0 *= l0; /* should be x^(k1)*exp(..) */
+  if (k0<k1) /* center steps; initially x^k*exp(...) */
+    for (k=k0; k<k1; k++)
+    { y1 *= l1; y0 *= l0;
+      I[k] = y1-y0;
+      Z[3*k] = k; Z[3*k+1] = cf[1]; Z[3*k+2] = 2*cf[2];
+    }
+   
+  y1 *= l1; y0 *= l0; /* should be x^(k1)*exp(..) */
+if (debug) printf("k1 %2d  y0 %8.5f  y1 %8.5f\n",k1,y0,y1);
+  for (k=k1; k<k2; k++)
+  { y1 *= l1; y0 *= l0;
+    I[k] = y1-y0;
+  }
+  I[k2] = I[k2+1] = 0.0;
+  for (k=k2-1; k>=k1; k--)
+    I[k] = (I[k]-cf[1]*I[k+1]-2*cf[2]*I[k+2])/(k+1);
+
+  if (k0<k1)
+  { I[k0] -= k0*I[k0-1];
+    I[k1-1] -= 2*cf[2]*I[k1];
+    Z[3*k0] = Z[3*k1-1] = 0;
+    solvetrid(&Z[3*k0],&I[k0],k1-k0);
+  }
+if (debug)
+{ printf("explinsid:\n");
+  for (k=0; k<p; k++) printf("  %8.5f\n",I[k]);
+}
+}
+
+void explinbkr(l0,l1,cf,I,p) /* small b,c; use back recursion */
+double l0, l1, *cf, *I;
+INT p;
+{ INT k, km;
+  double y0, y1;
+  y0 = lf_exp(cf[0]+l0*(cf[1]+cf[2]*l0));
+  y1 = lf_exp(cf[0]+l1*(cf[1]+cf[2]*l1));
+  km = p+10;
+  for (k=0; k<=km; k++)
+  { y1 *= l1; y0 *= l0;
+    I[k] = y1-y0;
+  }
+  I[km+1] = I[km+2] = 0;
+  for (k=km; k>=0; k--)
+    I[k] = (I[k]-cf[1]*I[k+1]-2*cf[2]*I[k+2])/(k+1);
+}
+
+void explinfbk0(l0,l1,cf,I,p) /* fwd and bac recur; b=0; c<0 */
+double l0, l1, *cf, *I;
+INT p;
+{ double y0, y1, f1, f2, f, ml2;
+  INT k, ks;
+
+  y0 = lf_exp(cf[0]+l0*l0*cf[2]);
+  y1 = lf_exp(cf[0]+l1*l1*cf[2]);
+  initi0i1(I,cf,y0,y1,l0,l1);
+
+  ml2 = MAX(l0*l0,l1*l1);
+  ks = 1+(INT)(2*fabs(cf[2])*ml2);
+  if (ks<2) ks = 2;
+  if (ks>p-3) ks = p;
+
+  /* forward recursion for k < ks */
+  for (k=2; k<ks; k++)
+  { y1 *= l1; y0 *= l0;
+    I[k] = (y1-y0-(k-1)*I[k-2])/(2*cf[2]);
+  }
+  if (ks==p) return;
+
+  y1 *= l1*l1; y0 *= l0*l0;
+  for (k=ks; k<p; k++) /* set I[k] = x^{k+1}e^(a+cx^2) | {l0,l1} */
+  { y1 *= l1; y0 *= l0;
+    I[k] = y1-y0;
+  }
+
+  /* initialize I[p-2] and I[p-1] */
+  f1 = 1.0/p; f2 = 1.0/(p-1);
+  I[p-1] *= f1; I[p-2] *= f2;
+  k = p; f = 1.0;
+  while (f>1.0e-8)
+  { y1 *= l1; y0 *= l0;
+    if ((k-p)%2==0) /* add to I[p-2] */
+    { f2 *= -2*cf[2]/(k+1);
+      I[p-2] += (y1-y0)*f2;
+    }
+    else /* add to I[p-1] */
+    { f1 *= -2*cf[2]/(k+1);
+      I[p-1] += (y1-y0)*f1;
+      f *= 2*fabs(cf[2])*ml2/(k+1);
+    }
+    k++;
+  }
+  
+  /* use back recursion for I[ks..(p-3)] */
+  for (k=p-3; k>=ks; k--)
+    I[k] = (I[k]-2*cf[2]*I[k+2])/(k+1);
+}
+
+void explinfbk(l0,l1,cf,I,p) /* fwd and bac recur; b not too large */
+double l0, l1, *cf, *I;
+INT p;
+{ double y0, y1;
+  INT k, ks, km;
+
+  y0 = lf_exp(cf[0]+l0*(cf[1]+l0*cf[2]));
+  y1 = lf_exp(cf[0]+l1*(cf[1]+l1*cf[2]));
+  initi0i1(I,cf,y0,y1,l0,l1);
+
+  ks = (INT)(3*fabs(cf[2]));
+  if (ks<3) ks = 3;
+  if (ks>0.75*p) ks = p; /* stretch the forward recurs as far as poss. */
+  /* forward recursion for k < ks */
+  for (k=2; k<ks; k++)
+  { y1 *= l1; y0 *= l0;
+    I[k] = (y1-y0-cf[1]*I[k-1]-(k-1)*I[k-2])/(2*cf[2]);
+  }
+  if (ks==p) return;
+
+  km = p+15;
+  y1 *= l1*l1; y0 *= l0*l0;
+  for (k=ks; k<=km; k++)
+  { y1 *= l1; y0 *= l0;
+    I[k] = y1-y0;
+  }
+  I[km+1] = I[km+2] = 0.0;
+  for (k=km; k>=ks; k--)
+    I[k] = (I[k]-cf[1]*I[k+1]-2*cf[2]*I[k+2])/(k+1);
+}
+
+void recent(I,resp,wt,p,s,x)
+double *I, *resp, *wt, x;
+INT p, s;
+{ INT i, j;
+
+  /* first, use W taylor series I -> resp */
+  for (i=0; i<=p; i++)
+  { resp[i] = 0.0;
+    for (j=0; j<s; j++) resp[i] += wt[j]*I[i+j];
+  }
+
+  /* now, recenter x -> 0 */
+  if (x==0) return;
+  for (j=0; j<=p; j++) for (i=p; i>j; i--) resp[i] += x*resp[i-1];
+}
+
+void recurint(l0,l2,cf,resp,p,ker)
+double l0, l2, *cf, *resp;
+INT p, ker;
+{ INT i, s;
+  double l1, d0, d1, d2, dl, z0, z1, z2, wt[20], ncf[3], I[50], r1[5], r2[5];
+if (debug) printf("\nrecurint: %8.5f %8.5f %8.5f   %8.5f %8.5f\n",cf[0],cf[1],cf[2],l0,l2);
+
+  if (cf[2]==0) /* go straight to explint1 */
+  { s = wtaylor(wt,0.0,ker);
+if (debug) printf("case 1\n");
+    explint1(l0,l2,cf,I,p+s);
+    recent(I,resp,wt,p,s,0.0);
+    return;
+  }
+
+  dl = l2-l0;
+  d0 = cf[1]+2*l0*cf[2];
+  d2 = cf[1]+2*l2*cf[2];
+  z0 = cf[0]+l0*(cf[1]+l0*cf[2]);
+  z2 = cf[0]+l2*(cf[1]+l2*cf[2]);
+
+  if ((fabs(cf[1]*dl)<1) && (fabs(cf[2]*dl*dl)<1))
+  { ncf[0] = z0; ncf[1] = d0; ncf[2] = cf[2];
+if (debug) printf("case 2\n");
+    s = wtaylor(wt,l0,ker);
+    explinbkr(0.0,dl,ncf,I,p+s);
+    recent(I,resp,wt,p,s,l0);
+    return;
+  }
+
+  if (fabs(cf[2]*dl*dl)<0.001) /* small c, use explint1+tay.ser */
+  { ncf[0] = z0; ncf[1] = d0; ncf[2] = cf[2];
+if (debug) printf("case small c\n");
+    s = wtaylor(wt,l0,ker);
+    explintyl(0.0,l2-l0,ncf,I,p+s);
+    recent(I,resp,wt,p,s,l0);
+    return;
+  }
+
+  if (d0*d2<=0) /* max/min in [l0,l2] */
+  { l1 = -cf[1]/(2*cf[2]);
+    z1 = cf[0]+l1*(cf[1]+l1*cf[2]);
+    d1 = 0.0;
+    if (cf[2]<0) /* peak, integrate around l1 */
+    { s = wtaylor(wt,l1,ker);
+      ncf[0] = z1; ncf[1] = 0.0; ncf[2] = cf[2];
+if (debug) printf("case peak  p %2d  s %2d\n",p,s);
+      explinfbk0(l0-l1,l2-l1,ncf,I,p+s);
+      recent(I,resp,wt,p,s,l1);
+      return;
+    }
+  }
+
+  if ((d0-2*cf[2]*dl)*(d2+2*cf[2]*dl)<0) /* max/min is close to [l0,l2] */
+  { l1 = -cf[1]/(2*cf[2]);
+    z1 = cf[0]+l1*(cf[1]+l1*cf[2]);
+    if (l1<l0) { l1 = l0; z1 = z0; }
+    if (l1>l2) { l1 = l2; z1 = z2; }
+
+    if ((z1>=z0) & (z1>=z2)) /* peak; integrate around l1 */
+    { s = wtaylor(wt,l1,ker);
+if (debug) printf("case 4\n");
+      d1 = cf[1]+2*l1*cf[2];
+      ncf[0] = z1; ncf[1] = d1; ncf[2] = cf[2];
+      explinfbk(l0-l1,l2-l1,ncf,I,p+s);
+      recent(I,resp,wt,p,s,l1);
+      return;
+    }
+
+    /* trough; integrate [l0,l1] and [l1,l2] */
+    for (i=0; i<=p; i++) r1[i] = r2[i] = 0.0;
+    if (l0<l1)
+    { s = wtaylor(wt,l0,ker);
+if (debug) printf("case 5\n");
+      ncf[0] = z0; ncf[1] = d0; ncf[2] = cf[2];
+      explinfbk(0.0,l1-l0,ncf,I,p+s);
+      recent(I,r1,wt,p,s,l0);
+    }
+    if (l1<l2)
+    { s = wtaylor(wt,l2,ker);
+if (debug) printf("case 6\n");
+      ncf[0] = z2; ncf[1] = d2; ncf[2] = cf[2];
+      explinfbk(l1-l2,0.0,ncf,I,p+s);
+      recent(I,r2,wt,p,s,l2);
+    }
+    for (i=0; i<=p; i++) resp[i] = r1[i]+r2[i];
+    return;
+  }
+
+  /* Now, quadratic is monotone on [l0,l2]; big b; moderate c */
+  if (z2>z0+3) /* steep increase, expand around l2 */
+  { s = wtaylor(wt,l2,ker);
+if (debug) printf("case 7\n");
+
+
+    ncf[0] = z2; ncf[1] = d2; ncf[2] = cf[2];
+    explinsid(l0-l2,0.0,ncf,I,p+s);
+    recent(I,resp,wt,p,s,l2);
+if (debug) printf("7 resp: %8.5f %8.5f %8.5f %8.5f\n",resp[0],resp[1],resp[2],resp[3]);
+    return;
+  }
+
+  /* bias towards expansion around l0, because it's often 0 */
+if (debug) printf("case 8\n");
+  s = wtaylor(wt,l0,ker);
+  ncf[0] = z0; ncf[1] = d0; ncf[2] = cf[2];
+  explinsid(0.0,l2-l0,ncf,I,p+s);
+  recent(I,resp,wt,p,s,l0);
+  return;
+}
+
+INT onedexpl(cf,mi,resp)
+double *cf, *resp;
+INT *mi;
+{ INT i;
+  double f0, fr, fl;
+  if (mi[MDEG]>=2) ERROR(("onedexpl only valid for deg=0,1"));
+  if (fabs(cf[1])>=EFACT) return(LF_BADP);
+
+  f0 = exp(cf[0]); fl = fr = 1.0;
+  for (i=0; i<=2*mi[MDEG]; i++)
+  { f0 *= i+1;
+    fl /=-(EFACT+cf[1]);
+    fr /=  EFACT-cf[1];
+    resp[i] = f0*(fr-fl);
+  }
+  return(LF_OK);
+}
+
+INT onedgaus(cf,mi,resp)
+double *cf, *resp;
+INT *mi;
+{ INT i;
+  double f0, mu, s2;
+  if (mi[MDEG]>=3)
+  { ERROR(("onedgaus only valid for deg=0,1,2"));
+    return(LF_ERR);
+  }
+  if (2*cf[2]>=GFACT*GFACT) return(LF_BADP);
+
+  s2 = 1/(GFACT*GFACT-2*cf[2]);
+  mu = cf[1]*s2;
+  resp[0] = 1.0;
+  if (mi[MDEG]>=1)
+  { resp[1] = mu;
+    resp[2] = s2+mu*mu;
+    if (mi[MDEG]==2)
+    { resp[3] = mu*(3*s2+mu*mu);
+      resp[4] = 3*s2*s2 + mu*mu*(6*s2+mu*mu);
+    }
+  }
+  f0 = S2PI * exp(cf[0]+mu*mu/(2*s2))*sqrt(s2);
+  for (i=0; i<=2*mi[MDEG]; i++) resp[i] *= f0;
+  return(LF_OK);
+}
+
+INT onedint(cf,mi,l0,l1,resp) /* int W(u)u^j exp(..), j=0..2*deg */
+double *cf, l0, l1, *resp;
+INT *mi;
+{ double u, uj, y, ncf[4], rr[5];
+  INT deg, i, j;
+    memset(rr, 0, sizeof(rr));
+if (debug) printf("onedint: %f %f %f   %f %f\n",cf[0],cf[1],cf[2],l0,l1);
+  deg = mi[MDEG];
+
+  if (deg<=2)
+  { for (i=0; i<3; i++) ncf[i] = (i>deg) ? 0.0 : cf[i];
+    ncf[2] /= 2;
+
+    if (mi[MKER]==WEXPL) return(onedexpl(ncf,mi,resp));
+    if (mi[MKER]==WGAUS) return(onedgaus(ncf,mi,resp));
+
+    if (l1>0)
+      recurint(MAX(l0,0.0),l1,ncf,resp,2*deg,mi[MKER]);
+    else for (i=0; i<=2*deg; i++) resp[i] = 0;
+
+    if (l0<0)
+    { ncf[1] = -ncf[1];
+      l0 = -l0; l1 = -l1;
+      recurint(MAX(l1,0.0),l0,ncf,rr,2*deg,mi[MKER]);
+    }
+    else for (i=0; i<=2*deg; i++) rr[i] = 0.0;
+
+    for (i=0; i<=2*deg; i++)
+      resp[i] += (i%2==0) ? rr[i] : -rr[i];
+
+    return(LF_OK);
+  }
+
+  /* For degree >= 3, we use Simpson's rule. */
+  for (j=0; j<=2*deg; j++) resp[j] = 0.0;
+  for (i=0; i<=mi[MMINT]; i++)
+  { u = l0+(l1-l0)*i/mi[MMINT];
+    y = cf[0]; uj = 1;
+    for (j=1; j<=deg; j++)
+    { uj *= u;
+      y += cf[j]*uj/fact[j];
+    }
+    y = (4-2*(i%2==0)-(i==0)-(i==mi[MMINT])) *
+          W(fabs(u),mi[MKER])*exp(MIN(y,300.0));
+    for (j=0; j<=2*deg; j++)
+    { resp[j] += y;
+      y *= u;
+    }
+  }
+  for (j=0; j<=2*deg; j++) resp[j] = resp[j]*(l1-l0)/(3*mi[MMINT]);
+  return(LF_OK);
+}
+
diff --git a/src/locfit/density.c b/src/locfit/density.c
new file mode 100644
index 0000000..45b4f60
--- /dev/null
+++ b/src/locfit/density.c
@@ -0,0 +1,509 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include "local.h"
+
+extern int lf_status;
+static double u[MXDIM], ilim[2*MXDIM], *ff;
+static lfit   *den_lf;
+static design *den_des;
+INT fact[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800};
+
+INT multint(), prodint(), gausint(), mlinint();
+
+void prresp(coef,resp,p)
+double *coef, *resp;
+INT p;
+{ INT i, j;
+  printf("Coefficients:\n");
+  for (i=0; i<p; i++) printf("%8.5f ",coef[i]);
+  printf("\n");
+  printf("Response matrix:\n");
+  for (i=0; i<p; i++)
+  { for (j=0; j<p; j++) printf("%9.6f, ",resp[i+j*p]);
+    printf("\n");
+  }
+}
+
+INT multint(t,resp1,resp2,lf,cf,h)
+lfit *lf;
+double *t, *resp1, *resp2, *cf, h;
+{ INT d, p, i, j, k, m, m1, w, z, z1;
+  double th, wt, dj[MXDIM];
+  d = lf->mi[MDIM]; p = lf->mi[MP];
+  setzero(resp1,p*p);
+  m = 1; m1 = lf->mi[MMINT]+1;
+  for (i=0; i<d; i++)
+  { m *= m1;
+    dj[i] = (ilim[i+d]-ilim[i])/lf->mi[MMINT];
+  }
+  for (i=0; i<m; i++)
+  { z = i; w = 1;
+    for (j=d-1; j>=0; j--)
+    { z1 = z%m1;
+      u[j] = t[j]+ilim[j]+dj[j]*z1;
+      w *= (4-2*(z1%2==0)-(z1==0)-(z1==lf->mi[MMINT]));
+      z /= m1;
+    }
+    wt = w*weight(lf,u,t,h,0,0.0);
+    if (wt>0)
+    { fitfun(lf,u,t,ff,NULL,0);
+      th = innerprod(ff,cf,p);
+      switch(lf->mi[MLINK])
+      { case LLOG:
+          addouter(resp1,ff,ff,p,wt*lf_exp(th));
+          break;
+        case LIDENT:
+          addouter(resp1,ff,ff,p,wt);
+          break;
+        default:
+          ERROR(("multint: Invalid link"));
+          return(LF_LNK);
+      }
+    }
+  }
+  wt = 1;
+  for (j=0; j<d; j++) wt *= dj[j]/3;
+  for (j=0; j<p; j++)
+    for (k=j; k<p; k++)
+      resp1[p*k+j] = resp1[p*j+k] = resp1[p*j+k]*wt;
+  return(LF_OK);
+}
+
+INT mlinint(t,resp1,resp2,lf,cf,h)
+lfit *lf;
+double *t, *resp1, *resp2, *cf, h;
+{
+  double hd, nb, wt, wu, g[4], w0, w1, v, *sca;
+  INT d, p, i, j, jmax, k, l, z, jj[2];
+  d = lf->mi[MDIM]; p = lf->mi[MP]; sca = lf->sca;
+  hd = 1;
+  for (i=0; i<d; i++) hd *= h*sca[i];
+
+  if (lf->mi[MLINK]==LIDENT)
+  { setzero(resp1,p*p);
+    resp1[0] = wint(d,NULL,0,lf->mi[MKER])*hd;
+    if (lf->mi[MDEG]==0) return(LF_OK);
+    jj[0] = 2; w0 = wint(d,jj,1,lf->mi[MKER])*hd*h*h;
+    for (i=0; i<d; i++) resp1[(i+1)*p+i+1] = w0*sca[i]*sca[i];
+    if (lf->mi[MDEG]==1) return(LF_OK);
+    for (i=0; i<d; i++)
+    { j = p-(d-i)*(d-i+1)/2;
+      resp1[j] = resp1[p*j] = w0*sca[i]*sca[i]/2;
+    }
+    if (d>1) { jj[1] = 2; w0 = wint(d,jj,2,lf->mi[MKER])*hd*h*h*h*h; }
+    jj[0] = 4; w1 = wint(d,jj,1,lf->mi[MKER])*hd*h*h*h*h/4;
+    z = d+1;
+    for (i=0; i<d; i++)
+    { k = p-(d-i)*(d-i+1)/2;
+      for (j=i; j<d; j++)
+      { l = p-(d-j)*(d-j+1)/2;
+        if (i==j) resp1[z*p+z] = w1*SQR(sca[i])*SQR(sca[i]);
+        else
+        { resp1[z*p+z] = w0*SQR(sca[i])*SQR(sca[j]);
+          resp1[k*p+l] = resp1[k+p*l] = w0/4*SQR(sca[i])*SQR(sca[j]);
+        }
+        z++;
+    } }
+    return(LF_OK);
+  }
+  switch(lf->mi[MDEG])
+  { case 0:
+      resp1[0] = lf_exp(cf[0])*wint(d,NULL,0,lf->mi[MKER])*hd;
+      return(LF_OK);
+    case 1:
+      nb = 0.0;
+      for (i=1; i<=d; i++)
+      { v = h*cf[i]*sca[i-1];
+        nb += v*v;
+      }
+      if (lf->mi[MKER]==WGAUS)
+      { w0 = 1/(GFACT*GFACT);
+        g[0] = lf_exp(cf[0]+w0*nb/2+d*log(S2PI/2.5));
+        g[1] = g[3] = g[0]*w0;
+        g[2] = g[0]*w0*w0;
+      }
+      else
+      { wt = wu = lf_exp(cf[0]);
+        w0 = wint(d,NULL,0,lf->mi[MKER]); g[0] = wt*w0;
+        g[1] = g[2] = g[3] = 0.0;
+        j = 0; jmax = (d+2)*lf->mi[MMINT];
+        while ((j<jmax) && (wt*w0/g[0]>1.0e-8))
+        { j++;
+          jj[0] = 2*j; w0 = wint(d,jj,1,lf->mi[MKER]);
+          if (d==1) g[3] += wt * w0;
+          else
+          { jj[0] = 2; jj[1] = 2*j-2; w1 = wint(d,jj,2,lf->mi[MKER]);
+            g[3] += wt*w1;
+            g[2] += wu*(w0-w1);
+          }
+          wt /= (2*j-1.0); g[1] += wt*w0;
+          wt *= nb/(2*j); g[0] += wt*w0;
+          wu /= (2*j-1.0)*(2*j);
+          if (j>1) wu *= nb;
+        }
+        if (j==jmax) WARN(("mlinint: series not converged"));
+      }
+      g[0] *= hd; g[1] *= hd;
+      g[2] *= hd; g[3] *= hd;
+      resp1[0] = g[0];
+      for (i=1; i<=d; i++)
+      { resp1[i] = resp1[(d+1)*i] = cf[i]*SQR(h*sca[i-1])*g[1];
+        for (j=1; j<=d; j++)
+        { resp1[(d+1)*i+j] = (i==j) ? g[3]*SQR(h*sca[i-1]) : 0;
+          resp1[(d+1)*i+j] += g[2]*SQR(h*h*sca[i-1]*sca[j-1])*cf[i]*cf[j];
+        }
+      }
+      return(LF_OK);
+  }
+  ERROR(("mlinint: deg=0,1 only"));
+  return(LF_ERR);
+}
+
+void prodint_resp(resp,prod_wk,dim,deg,p)
+double *resp, prod_wk[MXDIM][2*MXDEG+1];
+int dim, deg, p;
+{ double prod;
+  int i, j, k, j1, k1;
+
+  prod = 1.0;
+  for (i=0; i<dim; i++) prod *= prod_wk[i][0];
+  resp[0] += prod;
+  if (deg==0) return;
+
+  for (j1=1; j1<=deg; j1++)
+  { for (j=0; j<dim; j++)
+    { prod = 1.0;
+      for (i=0; i<dim; i++) prod *= prod_wk[i][j1*(j==i)];
+      prod /= fact[j1];
+      resp[1 + (j1-1)*dim +j] += prod;
+    }
+  }
+
+  for (k1=1; k1<=deg; k1++)
+    for (j1=k1; j1<=deg; j1++)
+    { for (k=0; k<dim; k++)
+        for (j=0; j<dim; j++)
+        { prod = 1.0;
+          for (i=0; i<dim; i++) prod *= prod_wk[i][k1*(k==i) + j1*(j==i)];
+          prod /= fact[k1]*fact[j1];
+          resp[ (1+(k1-1)*dim+k)*p + 1+(j1-1)*dim+j] += prod;
+        }
+    }
+}
+
+INT prodint(t,resp,resp2,lf,coef,h)
+lfit *lf;
+double *t, *resp, *resp2, *coef, h;
+{ INT dim, deg, p, i, j, k, st;
+  double cf[MXDEG+1], hj, hs, prod_wk[MXDIM][2*MXDEG+1];
+
+    st = 0;
+  dim = lf->mi[MDIM];
+  deg = lf->mi[MDEG];
+  p = lf->mi[MP];
+  for (i=0; i<p*p; i++) resp[i] = 0.0;
+  cf[0] = coef[0];
+
+/*  compute the one dimensional terms
+ */
+  for (i=0; i<dim; i++)
+  { hj = 1; hs = h*lf->sca[i];
+    for (j=0; j<deg; j++)
+    { hj *= hs;
+      cf[j+1] = hj*coef[ j*dim+i+1 ];
+    }
+    st = onedint(cf,lf->mi,ilim[i]/hs,ilim[i+dim]/hs,prod_wk[i]);
+    if (st==LF_BADP) return(st);
+    hj = 1;
+    for (j=0; j<=2*deg; j++)
+    { hj *= hs;
+      prod_wk[i][j] *= hj;
+    }
+    cf[0] = 0.0; /* so we only include it once, when d>=2 */
+  }
+
+/*  transfer to the resp array
+ */
+  prodint_resp(resp,prod_wk,dim,deg,p);
+
+/* Symmetrize.
+*/
+  for (k=0; k<p; k++)
+    for (j=k; j<p; j++)
+      resp[j*p+k] = resp[k*p+j];
+
+  return(st);
+}
+
+INT gausint(t,resp,C,cf,h,mi,sca)
+double *t, *resp, *C, *cf, h, *sca;
+INT *mi;
+{ double nb, det, z, *P;
+  INT d, p, i, j, k, l, m1, m2, f;
+  d = mi[MDIM]; p = mi[MP];
+  m1 = d+1; nb = 0;
+  P = &C[d*d];
+  resp[0] = 1;
+  for (i=0; i<d; i++)
+  { C[i*d+i] = SQR(GFACT/(h*sca[i]))-cf[m1++];
+    for (j=i+1; j<d; j++) C[i*d+j] = C[j*d+i] = -cf[m1++];
+  }
+  eig_dec(C,P,d);
+  det = 1;
+  for (i=1; i<=d; i++)
+  { det *= C[(i-1)*(d+1)];
+    if (det <= 0) return(LF_BADP);
+    resp[i] = cf[i];
+    for (j=1; j<=d; j++) resp[j+i*p] = 0;
+    resp[i+i*p] = 1;
+    svdsolve(&resp[i*p+1],u,P,C,P,d,0.0);
+  }
+  svdsolve(&resp[1],u,P,C,P,d,0.0);
+  det = sqrt(det);
+  for (i=1; i<=d; i++)
+  { nb += cf[i]*resp[i];
+    resp[i*p] = resp[i];
+    for (j=1; j<=d; j++)
+      resp[i+p*j] += resp[i]*resp[j];
+  }
+  m1 = d;
+  for (i=1; i<=d; i++)
+    for (j=i; j<=d; j++)
+    { m1++; f = 1+(i==j);
+      resp[m1] = resp[m1*p] = resp[i*p+j]/f;
+      m2 = d;
+      for (k=1; k<=d; k++)
+      { resp[m1+k*p] = resp[k+m1*p] =
+        ( resp[i]*resp[j*p+k] + resp[j]*resp[i*p+k]
+        + resp[k]*resp[i*p+j] - 2*resp[i]*resp[j]*resp[k] )/f;
+        for (l=k; l<=d; l++)
+        { m2++; f = (1+(i==j))*(1+(k==l));
+          resp[m1+m2*p] = resp[m2+m1*p] = ( resp[i+j*p]*resp[k+l*p]
+            + resp[i+k*p]*resp[j+l*p] + resp[i+l*p]*resp[j+k*p]
+            - 2*resp[i]*resp[j]*resp[k]*resp[l] )/f;
+    } } }
+  z = lf_exp(d*0.918938533+cf[0]+nb/2)/det;
+  multmatscal(resp,z,p*p);
+  return(LF_OK);
+}
+
+int likeden(coef, lk0, f1, A)
+double *coef, *lk0, *f1, *A;
+{ double lk, r;
+  int i, j, p, rstat;
+
+    lk = 0;
+  lf_status = LF_OK;
+  p = den_des->p;
+  if ((den_lf->mi[MLINK]==LIDENT) && (coef[0] != 0.0)) return(NR_BREAK);
+  lf_status = (den_des->itype)(den_des->xev,A,den_des->xtwx.Q,den_lf,coef,den_des->h);
+  if (lf_error) lf_status = LF_ERR;
+  if (lf_status==LF_BADP)
+  { *lk0 = -1.0e300;
+    return(NR_REDUCE);
+  }
+  if (lf_status!=LF_OK) return(NR_BREAK);
+  if (den_lf->mi[MDEB]>2) prresp(coef,A,(INT)p);
+
+  den_des->xtwx.p = p;
+  rstat = NR_OK;
+  switch(den_lf->mi[MLINK])
+  { case LLOG:
+      r = den_des->ss[0]/A[0];
+      coef[0] += log(r);
+      multmatscal(A,r,(INT)p*p);
+      A[0] = den_des->ss[0];
+      lk = -A[0];
+      if (fabs(coef[0]) > 700)
+      { lf_status = LF_OOB;
+        rstat = NR_REDUCE;
+      }
+      for (i=0; i<p; i++)
+      { lk += coef[i]*den_des->ss[i];
+        f1[i] = den_des->ss[i]-A[i];
+      }
+      break;
+    case LIDENT:
+      lk = 0.0;
+      for (i=0; i<p; i++)
+      { f1[i] = den_des->ss[i];
+        for (j=0; j<p; j++)
+          den_des->res[i] -= A[i*p+j]*coef[j];
+      }
+      break;
+  }
+  *lk0 = den_des->llk = lk;
+
+  return(rstat);
+}
+
+INT inre(x,bound,d)
+double *x, *bound;
+INT d;
+{ INT i, z;
+  z = 1;
+  for (i=0; i<d; i++)
+    if (bound[i]<bound[i+d])
+      z &= (x[i]>=bound[i]) & (x[i]<=bound[i+d]);
+  return(z);
+}
+
+INT setintlimits(lf, x, h, ang, lset)
+lfit *lf;
+INT *ang, *lset;
+double *x, h;
+{ INT d, i;
+  d = lf->mi[MDIM];
+  *ang = *lset = 0;
+  for (i=0; i<d; i++)
+  { if (lf->sty[i]==STANGL)
+    { ilim[i+d] = ((h<2) ? 2*asin(h/2) : PI)*lf->sca[i];
+      ilim[i] = -ilim[i+d];
+      *ang = 1;
+    }
+    else
+    { ilim[i+d] = h*lf->sca[i];
+      ilim[i] = -ilim[i+d];
+
+      if (lf->sty[i]==STLEFT) { ilim[i+d] = 0; *lset = 1; }
+      if (lf->sty[i]==STRIGH) { ilim[i] = 0;   *lset = 1; }
+
+      if (lf->xl[i]<lf->xl[i+d]) /* user limits for this variable */
+      { if (lf->xl[i]-x[i]> ilim[i])
+        { ilim[i] = lf->xl[i]-x[i]; *lset=1; }
+        if (lf->xl[i+d]-x[i]< ilim[i+d])
+        { ilim[i+d] = lf->xl[i+d]-x[i]; *lset=1; }
+      }
+    }
+    if (ilim[i]==ilim[i+d]) return(LF_DEMP); /* empty integration */
+  }
+  return(LF_OK);
+}
+
+INT selectintmeth(mi,lset,ang)
+INT *mi, lset, ang;
+{
+  if (mi[MIT]==IDEFA) /* select the default method */
+  { if (mi[MTG]==THAZ)
+    { if (ang) return(IDEFA);
+      return( IHAZD );
+    }
+
+    if (mi[MUBAS]) return(IMULT);
+
+    if (ang) return(IMULT);
+
+    if (iscompact(mi[MKER]))
+    { if (mi[MKT]==KPROD) return(IPROD);
+      if (lset)
+        return( (mi[MDIM]==1) ? IPROD : IMULT );
+      if (mi[MDEG]<=1) return(IMLIN);
+      if (mi[MDIM]==1) return(IPROD);
+      return(IMULT);
+    }
+
+    if (mi[MKER]==WGAUS)
+    { if (lset) WARN(("Integration for Gaussian weights ignores limits"));
+      if ((mi[MDIM]==1)|(mi[MKT]==KPROD)) return(IPROD);
+      return(IMLIN);
+    }
+
+    return(IDEFA);
+  }
+
+  /* user provided an integration method, check it is valid */
+
+  if (mi[MTG]==THAZ)
+  { if (ang) return(INVLD);
+    if (!iscompact(mi[MKER])) return(INVLD);
+    return( ((mi[MKT]==KPROD) | (mi[MKT]==KSPH)) ? IHAZD : INVLD );
+  }
+
+  if ((ang) && (mi[MIT] != IMULT)) return(INVLD);
+
+  switch(mi[MIT])
+  { case IMULT: return( iscompact(mi[MKER]) ? IMULT : INVLD );
+    case IPROD: return( ((mi[MDIM]==1) | (mi[MKT]==KPROD)) ? IPROD : INVLD );
+    case IMLIN: return( ((mi[MKT]==KSPH) && (!lset) &&
+      (mi[MDEG]<=1)) ? IMLIN : INVLD );
+  }
+
+  return(INVLD);
+}
+
+INT densinit(lf,des,h,cf,m)
+lfit *lf;
+design *des;
+double h, *cf;
+INT m;
+{ INT deg, p, i, ii, j, nnz, rnz, lset, ang, status;
+  double w;
+
+  den_lf  = lf;
+  den_des = des;
+
+  p = des->p; deg = lf->mi[MDEG];
+  ff = des->xtwx.wk;
+  cf[0] = NOSLN;
+  for (i=1; i<p; i++) cf[i] = 0.0;
+
+  if (!inre(des->xev,lf->xl,lf->mi[MDIM])) return(LF_XOOR);
+
+  status = setintlimits(lf,des->xev,h,&ang,&lset);
+  if (status != LF_OK) return(status);
+
+  switch(selectintmeth(lf->mi,lset,ang))
+  { case IMULT: des->itype = multint; break;
+    case IPROD: des->itype = prodint; break;
+    case IMLIN: des->itype = mlinint; break;
+    case IHAZD: des->itype = hazint; break;
+    case INVLD: ERROR(("Invalid integration method %d",lf->mi[MIT]));
+                break;
+    case IDEFA: ERROR(("No integration type available for this model"));
+                break;
+    default: ERROR(("densinit: unknown integral type"));
+  }
+
+  switch(deg)
+  { case 0: rnz = 1; break;
+    case 1: rnz = 1; break;
+    case 2: rnz = lf->mi[MDIM]+1; break;
+    case 3: rnz = lf->mi[MDIM]+2; break;
+    default: ERROR(("densinit: invalid degree %d",deg));
+  }
+  if (lf_error) return(LF_ERR);
+
+  setzero(des->ss,p);
+  nnz = 0;
+  for (i=0; i<m; i++)
+  { ii = des->ind[i];
+    if (!cens(lf,ii))
+    { w = des->w[i]*prwt(lf,ii);
+      for (j=0; j<p; j++) des->ss[j] += d_xij(des,i,j)*w;
+      if (des->w[i]>0.00001) nnz++;
+  } }
+
+  if (lf->mi[MTG]==THAZ) haz_init(lf,des,ilim);
+
+  if (lf->mi[MDEB]>2)
+  { printf("    LHS: ");
+    for (i=0; i<p; i++) printf(" %8.5f",des->ss[i]);
+    printf("\n");
+  }
+
+  switch(lf->mi[MLINK])
+  { case LIDENT:
+      cf[0] = 0.0;
+      return(LF_OK);
+    case LLOG:
+      if (nnz<rnz) { cf[0] = -1000; return(LF_DNOP); }
+      cf[0] = 0.0;
+      return(LF_OK);
+    default:
+      ERROR(("unknown link in densinit"));
+      return(LF_ERR);
+  }
+}
diff --git a/src/locfit/design.h b/src/locfit/design.h
new file mode 100644
index 0000000..a5bffbd
--- /dev/null
+++ b/src/locfit/design.h
@@ -0,0 +1,28 @@
+/*
+ *   Copyright (c) 1998-2000 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *   The design structure used in Locfit, and associated macro definitions.
+ */
+
+typedef struct {
+  vari *dw, *index;
+  double *xev;       /* fit point length p               */
+  double *X;         /* design matrix, length n*p        */
+  double *w, *di, *res, *th, *wd, h, xb[MXDIM];
+  double *V, *P, *f1, *ss, *oc, *cf, llk;
+  jacobian xtwx;     /* to store X'WVX and decomposition */
+  int cfn[1+MXDIM], ncoef;
+  int *fix;          /* indicator vector, showing fixed variables. */
+  INT *ind, n, p, pref, (*itype)();
+  INT (*vfun)();     /* pointer to the vertex processing function. */
+} design;
+
+#define cfn(des,i) (des->cfn[i])
+#define d_x(des) ((des)->X)
+#define d_xi(des,i) (&(des)->X[i*((des)->p)])
+#define d_xij(des,i,j) ((des)->X[i*((des)->p)+j])
+#define is_fixed(des,i) ((des)->fix[i]==1)
+
+extern int des_reqd(), des_reqi();
diff --git a/src/locfit/dist.c b/src/locfit/dist.c
new file mode 100644
index 0000000..7eec06c
--- /dev/null
+++ b/src/locfit/dist.c
@@ -0,0 +1,162 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include "local.h"
+
+#define LOG_2           0.6931471805599453094172321214581765680755
+#define IBETA_LARGE     1.0e30
+#define IBETA_SMALL     1.0e-30
+#define IGAMMA_LARGE    1.0e30
+#define DOUBLE_EP     2.2204460492503131E-16
+
+double dchisq(x, df)
+double x, df;
+{ return(exp(log(x/2)*(df/2-1) - x/2 - LGAMMA(df/2) - LOG_2));
+}
+
+double df(x, df1, df2)
+double x, df1, df2;
+{ double p;
+  p = exp(LGAMMA((df1+df2)/2) + df1/2*log(df1/df2) + (df1/2-1)*log(x)
+               - LGAMMA(df1/2) - LGAMMA(df2/2) - (df1+df2)/2*log(1+x*df1/df2));
+  return(p);
+}
+
+double ibeta(x, a, b)
+double x, a, b;
+{ int flipped = 0, i, k, count;
+  double I = 0, temp, pn[6], ak, bk, next, prev, factor, val;
+  if (x <= 0) return(0);
+  if (x >= 1) return(1);
+/* use ibeta(x,a,b) = 1-ibeta(1-x,b,z) */
+  if ((a+b+1)*x > (a+1))
+  { flipped = 1;
+    temp = a;
+    a = b;
+    b = temp;
+    x = 1 - x;
+  }
+  pn[0] = 0.0;
+  pn[2] = pn[3] = pn[1] = 1.0;
+  count = 1;
+  val = x/(1.0-x);
+  bk = 1.0;
+  next = 1.0;
+  do
+  { count++;
+    k = count/2;
+    prev = next;
+    if (count%2 == 0)
+      ak = -((a+k-1.0)*(b-k)*val)/((a+2.0*k-2.0)*(a+2.0*k-1.0));
+    else
+      ak = ((a+b+k-1.0)*k*val)/((a+2.0*k)*(a+2.0*k-1.0));
+    pn[4] = bk*pn[2] + ak*pn[0];
+    pn[5] = bk*pn[3] + ak*pn[1];
+    next = pn[4] / pn[5];
+    for (i=0; i<=3; i++)
+      pn[i] = pn[i+2];
+    if (fabs(pn[4]) >= IBETA_LARGE)
+      for (i=0; i<=3; i++)
+        pn[i] /= IBETA_LARGE;
+    if (fabs(pn[4]) <= IBETA_SMALL)
+      for (i=0; i<=3; i++)
+        pn[i] /= IBETA_SMALL;
+  } while (fabs(next-prev) > DOUBLE_EP*prev);
+  factor = a*log(x) + (b-1)*log(1-x);
+  factor -= LGAMMA(a+1) + LGAMMA(b) - LGAMMA(a+b);
+  I = exp(factor) * next;
+  return(flipped ? 1-I : I);
+}
+
+/*
+ * Incomplete gamma function.
+ * int_0^x u^{df-1} e^{-u} du / Gamma(df).
+ */
+double igamma(x, df)
+double x, df;
+{ double factor, term, gintegral, pn[6], rn, ak, bk;
+  int i, count, k;
+  if (x <= 0.0) return(0.0);
+
+  if (df < 1.0)
+    return( exp(df*log(x)-x-LGAMMA(df+1.0)) + igamma(x,df+1.0) );
+
+/* 
+ * this is unstable for large df
+ */
+  factor = exp(df*log(x) - x - LGAMMA(df));
+
+  if (x > 1.0 && x >= df)
+  {
+    pn[0] = 0.0;
+    pn[2] = pn[1] = 1.0;
+    pn[3] = x;
+    count = 1;
+    rn = 1.0 / x;
+    do
+    { count++;
+      k = count / 2;
+      gintegral = rn;
+      if (count%2 == 0)
+      { bk = 1.0;
+        ak = (double)k - df;
+      } else
+      { bk = x;
+        ak = (double)k;
+      }
+      pn[4] = bk*pn[2] + ak*pn[0];
+      pn[5] = bk*pn[3] + ak*pn[1];
+      rn = pn[4] / pn[5];
+      for (i=0; i<4; i++)
+        pn[i] = pn[i+2];
+      if (pn[4] > IGAMMA_LARGE)
+        for (i=0; i<4; i++)
+          pn[i] /= IGAMMA_LARGE;
+    } while (fabs(gintegral-rn) > DOUBLE_EP*rn);
+    gintegral = 1.0 - factor*rn;
+  }
+  else
+  { /*   For x<df, use the series
+     *   dpois(df,x)*( 1 + x/(df+1) + x^2/((df+1)(df+2)) + ... )
+     *   This could be slow if df large and x/df is close to 1.
+     */
+    gintegral = term = 1.0;
+    rn = df;
+    do
+    { rn += 1.0;
+      term *= x/rn;
+      gintegral += term;
+    } while (term > DOUBLE_EP*gintegral);
+    gintegral *= factor/df;
+  }
+  return(gintegral);
+}
+
+double pf(q, df1, df2)
+double q, df1, df2;
+{ return(ibeta(q*df1/(df2+q*df1), df1/2, df2/2));
+}
+
+double pchisq(q, df)
+double q, df;
+{ return(igamma(q/2, df/2));
+}
+
+#ifdef RVERSION
+extern double Rf_pnorm5();
+double pnorm(x,mu,s)
+double x, mu, s;
+{ return(Rf_pnorm5(x, mu, s, 1L, 0L));
+}
+#else
+double pnorm(x,mu,s)
+double x, mu, s;
+{ if(x == mu)
+    return(0.5);
+  x = (x-mu)/s;
+  if(x > 0) return((1 + erf(x/SQRT2))/2);
+  return(erfc(-x/SQRT2)/2);
+}
+#endif
diff --git a/src/locfit/ev_atree.c b/src/locfit/ev_atree.c
new file mode 100644
index 0000000..b659738
--- /dev/null
+++ b/src/locfit/ev_atree.c
@@ -0,0 +1,204 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *   This file contains functions for constructing and
+ *   interpolating the adaptive tree structure. This is
+ *   the default evaluation structure used by Locfit.
+ */
+
+#include "local.h"
+
+/*
+  Guess the number of fitting points.
+  Needs improving!
+*/
+void atree_guessnv(nvm,ncm,vc,dp,mi)
+double *dp;
+INT *nvm, *ncm, *vc, *mi;
+{ double a0, cu, ifl;
+  int i, nv, nc;
+
+  *ncm = 1<<30; *nvm = 1<<30;
+  *vc = 1 << mi[MDIM];
+
+  if (dp[DALP]>0)
+  { a0 = (dp[DALP] > 1) ? 1 : 1/dp[DALP];
+    if (dp[DCUT]<0.01)
+    { WARN(("guessnv: cut too small."));
+      dp[DCUT] = 0.01;
+    }
+    cu = 1;
+    for (i=0; i<mi[MDIM]; i++) cu *= MIN(1.0,dp[DCUT]);
+    nv = (int)((5*a0/cu+1)**vc);  /* this allows 10*a0/cu splits */
+    nc = (int)(10*a0/cu+1);      /* and 10*a0/cu cells */
+    if (nv<*nvm) *nvm = nv;
+    if (nc<*ncm) *ncm = nc;
+  }
+
+  if (*nvm == 1<<30) /* by default, allow 100 splits */
+  { *nvm = 102**vc;
+    *ncm = 201;
+  }
+
+  /* inflation based on mi[MK] */
+  ifl = mi[MK]/100.0;
+  *nvm = (int)(ifl**nvm);
+  *ncm = (int)(ifl**ncm);
+  
+}
+
+/*
+  Determine whether a cell in the tree needs splitting.
+  If so, return the split variable (0..d-1).
+  Otherwise, return -1.
+*/
+INT atree_split(lf,ce,le,ll,ur)
+lfit *lf;
+INT *ce;
+double *le, *ll, *ur;
+{ INT d, vc, i, is;
+  double h, hmin, score[MXDIM];
+    memset(score, 0, sizeof(score));
+  d = lf->mi[MDIM]; vc = 1<<d;
+
+  hmin = 0.0;
+  for (i=0; i<vc; i++)
+  { h = lf->h[ce[i]];
+    if ((h>0) && ((hmin==0)|(h<hmin))) hmin = h;
+  }
+
+  is = 0;
+  for (i=0; i<d; i++)
+  { le[i] = (ur[i]-ll[i])/lf->sca[i];
+    if ((lf->sty[i]==STCPAR) || (hmin==0))
+      score[i] = 2*(ur[i]-ll[i])/(lf->fl[i+d]-lf->fl[i]);
+    else
+      score[i] = le[i]/hmin;
+    if (score[i]>score[is]) is = i;
+  }
+  if (lf->dp[DCUT]<score[is]) return(is);
+  return(-1);
+}
+
+/*
+  recursively grow the tree structure, begining with the parent cell.
+*/
+void atree_grow(des,lf,ce,ct,term,ll,ur)
+design *des;
+lfit *lf;
+INT *ce, *ct, *term;
+double *ll, *ur;
+{ INT i, i0, i1, d, vc, ns, tk, nce[1024], pv;
+  double le[MXDIM], z;
+  d = lf->mi[MDIM]; vc = 1<<d;
+
+  /* does this cell need splitting?
+     If not, wrap up and return.
+  */
+  ns = atree_split(lf,ce,le,ll,ur);
+  if (ns==-1)
+  { if (ct != NULL) /* reconstructing terminal cells */
+    { for (i=0; i<vc; i++) term[*ct*vc+i] = ce[i];
+      (*ct)++;
+    }
+    return;
+  }
+
+  /* split the cell at the midpoint on side ns */
+  tk = 1<<ns;
+  for (i=0; i<vc; i++)
+  { if ((i&tk)==0) nce[i] = ce[i];
+    else
+    { i0 = ce[i];
+      i1 = ce[i-tk];
+      pv = (lf->sty[i]!=STCPAR) &&
+           (le[ns] < (lf->dp[DCUT]*MIN(lf->h[i0],lf->h[i1])));
+      nce[i] = newsplit(des,lf,i0,i1,pv);
+      if (lf_error) return;
+    }
+  }
+  z = ur[ns]; ur[ns] = (z+ll[ns])/2;
+  atree_grow(des,lf,nce,ct,term,ll,ur);
+  if (lf_error) return;
+  ur[ns] = z;
+  for (i=0; i<vc; i++)
+    nce[i] = ((i&tk)== 0) ? nce[i+tk] : ce[i];
+  z = ll[ns]; ll[ns] = (z+ur[ns])/2;
+  atree_grow(des,lf,nce,ct,term,ll,ur);
+  ll[ns] = z;
+}
+
+void atree_start(des,lf)
+design *des;
+lfit *lf;
+{ INT i, j, vc, d, ncm, nvm, k;
+  double ll[MXDIM], ur[MXDIM];
+
+  d = lf->mi[MDIM];
+  atree_guessnv(&nvm,&ncm,&vc,lf->dp,lf->mi);
+  trchck(lf,nvm,ncm,d,des->p,vc);
+
+  /* Set the lower left, upper right limits. */
+  for (j=0; j<d; j++)
+  { ll[j] = lf->fl[j];
+    ur[j] = lf->fl[j+d];
+  }
+
+  /* Set the initial cell; fit at the vertices. */
+  for (i=0; i<vc; i++)
+  { j = i;
+    for (k=0; k<d; ++k)
+    { evptx(lf,i,k) = (j%2) ? ur[k] : ll[k];
+      j >>= 1;
+    }
+    lf->ce[i] = i;
+    des->vfun(des,lf,i);
+    if (lf_error) return;
+    lf->s[i] = 0;
+  }
+  lf->nv = vc;
+
+  /* build the tree */
+  atree_grow(des,lf,lf->ce,NULL,NULL,ll,ur);
+  lf->nce = 1;
+}
+
+double atree_int(tr,x,what)
+lfit *tr;
+double *x;
+INT what;
+{ double vv[64][64], *ll, *ur, h, xx[MXDIM];
+  INT d, i, lo, tk, ns, nv, nc, vc, ce[64];
+  d = tr->mi[MDIM];
+  vc = 1<<tr->mi[MDIM];
+  for (i=0; i<vc; i++)
+  { setzero(vv[i],vc);
+    nc = exvval(tr,vv[i],i,d,what,1);
+    ce[i] = tr->ce[i];
+  }
+  ns = 0;
+  while(ns!=-1)
+  { ll = evpt(tr,ce[0]); ur = evpt(tr,ce[vc-1]);
+    ns = atree_split(tr,ce,xx,ll,ur);
+    if (ns!=-1)
+    { tk = 1<<ns;
+      h = ur[ns]-ll[ns];
+      lo = (2*(x[ns]-ll[ns])) < h;
+      for (i=0; i<vc; i++) if ((tk&i)==0)
+      { nv = newsplit((design *)NULL,tr,ce[i],ce[i+tk],0);
+        if (lf_error) return(0.0);
+        if (lo)
+        { ce[i+tk] = nv;
+          if (tr->s[nv]) exvvalpv(vv[i+tk],vv[i],vv[i+tk],d,ns,h,nc);
+                    else exvval(tr,vv[i+tk],nv,d,what,1);
+        }
+        else
+        { ce[i] = nv;
+          if (tr->s[nv]) exvvalpv(vv[i],vv[i],vv[i+tk],d,ns,h,nc);
+                    else exvval(tr,vv[i],nv,d,what,1);
+      } }
+  } }
+  ll = evpt(tr,ce[0]); ur = evpt(tr,ce[vc-1]);
+  return(rectcell_interp(x,vdptr(tr->xxev),vv,ll,ur,d,nc));
+}
diff --git a/src/locfit/ev_interp.c b/src/locfit/ev_interp.c
new file mode 100644
index 0000000..badc23e
--- /dev/null
+++ b/src/locfit/ev_interp.c
@@ -0,0 +1,273 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include "local.h"
+
+double linear_interp(h,d,f0,f1)
+double h, d, f0, f1;
+{ if (d==0) return(f0);
+  return( ( (d-h)*f0 + h*f1 ) / d );
+}
+
+void hermite2(x,z,phi)
+double x, z, *phi;
+{ double h;
+  if (z==0)
+  { phi[0] = 1.0; phi[1] = phi[2] = phi[3] = 0.0;
+    return;
+  }
+  h = x/z;
+  if (h<0)
+  { phi[0] = 1; phi[1] = 0;
+    phi[2] = h; phi[3] = 0;
+    return;
+  }
+  if (h>1)
+  { phi[0] = 0; phi[1] = 1;
+    phi[2] = 0; phi[3] = h-1;
+    return;
+  }
+  phi[1] = h*h*(3-2*h);
+  phi[0] = 1-phi[1];
+  phi[2] = h*(1-h)*(1-h);
+  phi[3] = h*h*(h - 1);
+}
+
+double cubic_interp(h,f0,f1,d0,d1)
+double h, f0, f1, d0, d1;
+{ double phi[4];
+  hermite2(h,1.0,phi);
+  return(phi[0]*f0+phi[1]*f1+phi[2]*d0+phi[3]*d1);
+}
+
+double cubintd(h,f0,f1,d0,d1)
+double h, f0, f1, d0, d1;
+{ double phi[4];
+  phi[1] = 6*h*(1-h);
+  phi[0] = -phi[1];
+  phi[2] = (1-h)*(1-3*h);
+  phi[3] = h*(3*h-2);
+  return(phi[0]*f0+phi[1]*f1+phi[2]*d0+phi[3]*d1);
+}
+
+/*
+  interpolate over a rectangular cell.
+    x = interpolation point. 
+    xev = evaluation points (do I need this?)
+    vv = array of vertex values.
+    ll = lower left corner.
+    ur = upper right corner.
+    d = dimension.
+    nc = no of coefficients.
+*/
+double rectcell_interp(x,xev,vv,ll,ur,d,nc)
+double *x, *xev, vv[64][64], *ll, *ur;
+INT d, nc;
+{ double phi[4];
+  INT i, j, k, tk;
+
+  tk = 1<<d;
+  for (i=0; i<tk; i++) if (vv[i][0]==NOSLN) return(NOSLN);
+
+  /* no derivatives - use multilinear interpolation */
+  if (nc==1)
+  { for (i=d-1; i>=0; i--)
+    { tk = 1<<i;
+      for (j=0; j<tk; j++)
+        vv[j][0] = linear_interp(x[i]-ll[i],ur[i]-ll[i],vv[j][0],vv[j+tk][0]);
+    }
+    return(vv[0][0]);
+  }
+
+  /* with derivatives -- use cubic */
+  if (nc==d+1)
+  { for (i=d-1; i>=0; i--)
+    { hermite2(x[i]-ll[i],ur[i]-ll[i],phi);
+      tk = 1<<i;
+      phi[2] *= ur[i]-ll[i];
+      phi[3] *= ur[i]-ll[i];
+      for (j=0; j<tk; j++)
+      { vv[j][0] = phi[0]*vv[j][0] + phi[1]*vv[j+tk][0]
+                 + phi[2]*vv[j][i+1] + phi[3]*vv[j+tk][i+1];
+        for (k=1; k<=i; k++)
+          vv[j][k] = phi[0]*vv[j][k] + phi[1]*vv[j+tk][k];
+      }
+    }
+    return(vv[0][0]); 
+  }
+
+  /* with all coefs -- use multicubic */
+  for (i=d-1; i>=0; i--)
+  { hermite2(x[i]-ll[i],ur[i]-ll[i],phi);
+    tk = 1<<i;
+    phi[2] *= ur[i]-ll[i];
+    phi[3] *= ur[i]-ll[i];
+    for (j=0; j<tk; j++)
+      for (k=0; k<tk; k++)
+        vv[j][k] = phi[0]*vv[j][k] + phi[1]*vv[j+tk][k]
+                 + phi[2]*vv[j][k+tk] + phi[3]*vv[j+tk][k+tk];
+  }
+  return(vv[0][0]);
+}
+
+INT exvval(lf,vv,nv,d,what,z)
+lfit *lf;
+double *vv;
+INT nv, d, what, z;
+{ INT i, k;
+  double *values;
+  k = (z) ? 1<<d : d+1;
+  for (i=1; i<k; i++) vv[i] = 0.0;
+  switch(what)
+  { case PCOEF:
+      values = lf->coef;
+      break;
+    case PVARI:
+    case PNLX:
+      values = lf->nlx;
+      break;
+    case PT0:
+      values = lf->t0;
+      break;
+    case PBAND:
+      vv[0] = lf->h[nv];
+      return(1);
+    case PDEGR:
+      vv[0] = lf->deg[nv];
+      return(1);
+    case PLIK:
+      vv[0] = lf->lik[nv];
+      return(1);
+    case PRDF:
+      vv[0] = lf->lik[2*lf->nvm+nv];
+      return(1);
+    default:
+      ERROR(("Invalid what in exvval"));
+      return(0);
+  }
+  vv[0] = values[nv];
+  if ((lf->mi[MDEG]==0) && (lf->mi[MDC]==0))return(1);
+  if (z)
+  { for (i=0; i<d; i++) vv[1<<i] = values[(i+1)*lf->nvm+nv];
+    return(1<<d);
+  }
+  else
+  { for (i=1;i<=d; i++) vv[i] = values[i*lf->nvm+nv];
+    return(d+1);
+  }
+}
+
+void exvvalpv(vv,vl,vr,d,k,dl,nc)
+double *vv, *vl, *vr, dl;
+INT d, k, nc;
+{ INT i, tk, td;
+  double f0, f1;
+  if (nc==1)
+  { vv[0] = (vl[0]+vr[0])/2;
+    return;
+  }
+  tk = 1<<k;
+  td = 1<<d;
+  for (i=0; i<td; i++) if ((i&tk)==0)
+  { f0 = (vl[i]+vr[i])/2 + dl*(vl[i+tk]-vr[i+tk])/8;
+    f1 = 1.5*(vr[i]-vl[i])/dl - (vl[i+tk]+vr[i+tk])/4;
+    vv[i] = f0;
+    vv[i+tk] = f1;
+  }
+} 
+
+double gridint(tr,x,what)
+lfit *tr;
+double *x;
+INT what;
+{ INT d, i, j, jj, v[MXDIM], vc, z0, sk, nce[1024], nc;
+  double *ll, *ur, vv[64][64];
+  d = tr->mi[MDIM];
+  ll = evpt(tr,0); ur = evpt(tr,tr->nv-1);
+  z0 = 0; vc = 1<<d;
+  for (j=d-1; j>=0; j--)
+  { v[j] = (INT)((tr->mg[j]-1)*(x[j]-ll[j])/(ur[j]-ll[j]));
+    if (v[j]<0) v[j]=0;
+    if (v[j]>=tr->mg[j]-1) v[j] = tr->mg[j]-2;
+    z0 = z0*tr->mg[j]+v[j];
+  }
+  nce[0] = z0; nce[1] = z0+1; sk = jj = 1; 
+    memset(nce, 0, sizeof(nce));
+  for (i=1; i<d; i++)
+  { sk *= tr->mg[i-1];
+    jj<<=1;
+    for (j=0; j<jj; j++)
+      nce[j+jj] = nce[j]+sk;
+  }
+    nc = 0;
+  for (i=0; i<vc; i++)
+    nc = exvval(tr,vv[i],nce[i],d,what,1);
+  ll = evpt(tr,nce[0]);
+  ur = evpt(tr,nce[vc-1]);
+  return(rectcell_interp(x,vdptr(tr->xxev),vv,ll,ur,d,nc));
+}
+
+double fitpint(lf,x,what,i)
+lfit *lf;
+double *x;
+INT what, i;
+{ double vv[1+MXDIM];
+  exvval(lf,vv,i,lf->mi[MDIM],what,0);
+  return(vv[0]);
+}
+
+double dointpointpf(lf,des,x,what)
+lfit *lf;
+design *des;
+double *x;
+INT what;
+{ locfit(lf,des,0.0,0);
+  if (what==PCOEF) return(des->cf[0]);
+  if ((what==PNLX)|(what==PT0)) return(sqrt(comp_infl(lf,des)));
+  ERROR(("dointpointpf: invalid what"));
+  return(0.0);
+}
+
+double xbarint(lf,x,what)
+lfit *lf;
+double *x;
+INT what;
+{ INT i, nc;
+  double vv[1+MXDIM], f;
+  nc = exvval(lf,vv,0,lf->mi[MDIM],what,0);
+  f = vv[0];
+  if (nc>1)
+    for (i=0; i<lf->mi[MDIM]; i++)
+      f += vv[i+1]*(x[i]-evptx(lf,0,i));
+  return(f);
+}
+
+double dointpoint(lf,des,x,what,ev,j)
+lfit *lf;
+design *des;
+double *x;
+INT what, ev, j;
+{ double xf, f;
+  INT i;
+  for (i=0; i<lf->mi[MDIM]; i++) if (lf->sty[i]==STANGL)
+  { xf = floor(x[i]/(2*PI*lf->sca[i]));
+    x[i] -= xf*2*PI*lf->sca[i];
+  }
+    f = 0;
+  if (ident==1) return(dointpointpf(lf,des,x,what));
+  switch(ev)
+  { case EGRID: f = gridint(lf,x,what); break;
+    case EKDTR: f = kdtre_int(lf,x,what); break;
+    case ETREE: f = atree_int(lf,x,what); break;
+    case EPHULL: f = triang_int(lf,x,what); break;
+    case EFITP: f = fitpint(lf,x,what,j); break;
+    case EXBAR: f = xbarint(lf,x,what); break;
+    case ENONE: f = 0; break;
+    default: ERROR(("dointpoint: cannot interpolate this structure"));
+  }
+  if (((what==PT0)|(what==PNLX)) && (f<0)) f = 0.0;
+  f += addparcomp(lf,x,what);
+  return(f);
+}
diff --git a/src/locfit/ev_kdtre.c b/src/locfit/ev_kdtre.c
new file mode 100644
index 0000000..5dbada6
--- /dev/null
+++ b/src/locfit/ev_kdtre.c
@@ -0,0 +1,332 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *   Routines for building and interpolating the kd tree.
+ *   Initially, this started from the loess code.
+ *
+ *   Todo: EKDCE isn't working.
+ */
+
+#include "local.h"
+
+void newcell();
+static INT nterm;
+
+void kdtre_guessnv(nvm,ncm,vc,dp,mi)
+double *dp;
+INT *nvm, *ncm, *vc, *mi;
+{ int k;
+  if (mi[MEV] == EKDTR)
+  { nterm = (INT) (dp[DCUT]/4 * mi[MN] * MIN(dp[DALP],1.0) );
+    k = 2*mi[MN]/nterm;
+    *vc = 1<<mi[MDIM];
+    *ncm = 2*k+1;
+    *nvm = (k+2)**vc/2;
+    return;
+  }
+  if (mi[MEV] == EKDCE)
+  { nterm = (INT) (mi[MN] * dp[DALP]);
+    *vc = 1;
+    *nvm = 1+(INT)(2*mi[MN]/nterm);
+    *ncm = 2**nvm+1;
+    return;
+  }
+  *nvm = *ncm = *vc = 0;
+  return;
+}
+
+/*
+  Split x[pi[l..r]] into two equal sized sets.
+
+  Let m=(l+r)/2.
+  At return,
+    x[pi[l..m]] < x[pi[m+1..r]].
+    Assuming no ties:
+      If l+r is odd, the sets have the same size.
+      If l+r is even, the low set is larger by 1.
+    If there are ties, all ties go in the low set.
+*/      
+int ksmall(l, r, m, x, pi)
+INT l, r, m, *pi;
+double *x;
+{
+  int il, ir, jl, jr;
+  double t;
+
+
+  while(l<r)
+  { t = x[pi[m]];
+
+    /*
+      permute the observations so that
+        x[pi[l..il]] < t <= x[pi[ir..r]].
+    */
+    ir = l; il = r;
+    while (ir<il)
+    { while ((ir<=r) && (x[pi[ir]] < t)) ir++;
+      while ((il>=l) && (x[pi[il]]>= t)) il--;
+      if (ir<il) ISWAP(pi[ir],pi[il]);
+    }
+
+    /*
+      move  = t to the middle:
+        x[pi[l..il]]  < t
+        x[pi[jl..jr]] = t
+        x[pi[ir..r]] > t
+    */
+    jl = ir; jr = r;
+    while (ir<jr)
+    { while ((ir<=r)  && (x[pi[ir]]== t)) ir++;
+      while ((jr>=jl) && (x[pi[jr]] > t)) jr--;
+      if (ir<jr) ISWAP(pi[ir],pi[jr]);
+    }
+
+    /*
+      we're done if m is in the middle, jl <= m <= jr.
+    */
+    if ((jl<=m) & (jr>=m)) return(jr);
+
+    /*
+      update l or r.
+    */
+    if (m>=ir) l = ir;
+    if (m<=il) r = il;
+  }
+  if (l==r) return(l);
+  ERROR(("ksmall failure"));
+  return(0);
+}
+
+INT terminal(tr,p,pi,fc,d,m,split_val)
+lfit *tr;
+INT p, *pi, d, fc, *m;
+double *split_val;
+{ INT i, k, lo, hi, split_var;
+  double max, min, score, max_score, t;
+
+  /*
+    if there are fewer than fc points in the cell, this cell
+    is terminal.
+  */
+  lo = tr->lo[p]; hi = tr->hi[p];
+  if (hi-lo < fc) return(-1);
+
+  /* determine the split variable */
+  max_score = 0.0; split_var = 0;
+  for (k=0; k<d; k++)
+  { max = min = datum(tr, k, pi[lo]);
+    for (i=lo+1; i<=hi; i++)
+    { t = datum(tr,k,pi[i]);
+      if (t<min) min = t;
+      if (t>max) max = t;
+    }
+    score = (max-min) / tr->sca[k];
+    if (score > max_score)
+    { max_score = score;
+      split_var = k;
+    }
+  }
+  if (max_score==0) /* all points in the cell are equal */
+    return(-1);
+
+  *m = ksmall(lo,hi,(lo+hi)/2, dvari(tr,split_var), pi);
+  *split_val = datum(tr, split_var, pi[*m]);
+
+  if (*m==hi) /* all observations go lo */
+    return(-1);
+  return(split_var);
+}
+
+void kdtre_start(des,tr)
+design *des;
+lfit *tr;
+{ INT i, j, vc, d, nc, nv, ncm, nvm, k, m, n, p, *pi;
+  double sv;
+  d = tr->mi[MDIM]; n = tr->mi[MN]; pi = des->ind;
+  kdtre_guessnv(&nvm,&ncm,&vc,tr->dp,tr->mi);
+  trchck(tr,nvm,ncm,d,des->p,vc);
+
+  nv = 0;
+  if (tr->mi[MEV] != EKDCE)
+  { for (i=0; i<vc; i++)
+    { j = i;
+      for (k=0; k<d; ++k)
+      { evptx(tr,i,k) = tr->fl[d*(j%2)+k];
+        j >>= 1;
+      }
+    }
+    nv = vc;
+    for (j=0; j<vc; j++) tr->ce[j] = j;
+  }
+
+  for (i=0; i<n; i++) pi[i] = i;
+  p = 0; nc = 1;
+  tr->lo[p] = 0; tr->hi[p] = n-1;
+  tr->s[p] = -1;
+  while (p<nc)
+  { k = terminal(tr,p,pi,nterm,d,&m,&sv);
+    if (k>=0)
+    {
+      if ((ncm<nc+2) | (2*nvm<2*nv+vc))
+      { WARN(("Insufficient space for full tree"));
+        tr->nce = nc; tr->nv = nv;
+        return;
+      }
+
+      /* new lo cell has obsn's tr->lo[p]..m */
+      tr->lo[nc] = tr->lo[p];
+      tr->hi[nc] = m;
+      tr->s[nc] = -1;
+
+      /* new hi cell has obsn's m+1..tr->hi[p] */
+      tr->lo[nc+1] = m+1;
+      tr->hi[nc+1] = tr->hi[p];
+      tr->s[nc+1] = -1;
+
+      /* cell p is split on variable k, value sv */
+      tr->s[p] = k;
+      tr->sv[p] = sv;
+      tr->lo[p] = nc; tr->hi[p] = nc+1;
+
+      nc=nc+2; i = nv;
+
+      /* now compute the new vertices. */
+      if (tr->mi[MEV] != EKDCE)
+        newcell(&nv,vc,vdptr(tr->xxev), d, k, sv,
+             &tr->ce[p*vc], &tr->ce[(nc-2)*vc], &tr->ce[(nc-1)*vc]);
+
+    }
+    else if (tr->mi[MEV]==EKDCE) /* new vertex at cell center */
+    { sv = 0;
+      for (i=0; i<d; i++) evptx(tr,nv,i) = 0;
+      for (j=tr->lo[p]; j<=tr->hi[p]; j++)
+      { sv += prwt(tr,pi[j]);
+        for (i=0; i<d; i++)
+          evptx(tr,nv,i) += datum(tr,i,pi[j])*prwt(tr,pi[j]);
+      }
+      for (i=0; i<d; i++) evptx(tr,nv,i) /= sv;
+      tr->mi[MN] = tr->hi[p]-tr->lo[p]+1;
+      des->ind = &pi[tr->lo[p]];
+      des->vfun(des,tr,nv);
+      tr->mi[MN] = n; des->ind = pi;
+      nv++;
+    }
+    p++;
+  }
+
+  /* We've built the tree. Now do the fitting. */
+  if (tr->mi[MEV]==EKDTR)
+    for (i=0; i<nv; i++) des->vfun(des,tr,i);
+
+  tr->nce = nc; tr->nv = nv;
+  return;
+}
+
+void newcell(nv,vc,xev, d, k, split_val, cpar, clef, crig)
+double *xev, split_val;
+INT *nv, vc, d, k, *cpar, *clef, *crig;
+{ INT i, ii, j, j2, tk, match;
+  tk = 1<<k;
+  for (i=0; i<vc; i++)
+  { if ((i&tk) == 0)
+    { for (j=0; j<d; j++) xev[*nv*d+j] = xev[d*cpar[i]+j];
+      xev[*nv*d+k] = split_val;
+      match = 0; j = vc; /* no matches in first vc points */
+      while ((j<*nv) && (!match))
+      { j2 = 0;
+        while ((j2<d) && (xev[*nv*d+j2] == xev[j*d+j2])) j2++;
+        match = (j2==d);
+        if (!match) j++;
+      }
+      ii = i+tk;
+      clef[i] = cpar[i];
+      clef[ii]= crig[i] = j;
+      crig[ii]= cpar[ii];
+      if (!match) (*nv)++;
+    }
+  }
+  return;
+}
+
+extern void hermite2();
+
+double blend(lf,s,x,ll,ur,j,nt,t,what)
+lfit *lf;
+double s, *x, *ll, *ur;
+INT j, nt, *t, what;
+{ INT k, k1, m, nc, j0, j1, *ce;
+  double v0, v1, xibar, g0[3], g1[3], gg[4], gp[4], phi[4];
+  ce = lf->ce;
+  for (k=0; k<4; k++)  /* North South East West */
+  { k1 = (k>1);
+    v0 = ll[k1]; v1 = ur[k1];
+    j0 = ce[j+2*(k==0)+(k==2)];
+    j1 = ce[j+3-2*(k==1)-(k==3)];
+    xibar = (k%2==0) ? ur[k<2] : ll[k<2];
+    m = nt;
+    while ((m>=0) && ((lf->s[t[m]] != (k<=1)) | (lf->sv[t[m]] != xibar))) m--;
+    if (m >= 0)
+    { m = (k%2==1) ? lf->lo[t[m]] : lf->hi[t[m]];
+      while (lf->s[m] != -1)
+        m = (x[lf->s[m]] < lf->sv[m]) ? lf->lo[m] : lf->hi[m];
+      if (v0 < evptx(lf,ce[4*m+2*(k==1)+(k==3)],k1))
+      { j0 = ce[4*m+2*(k==1)+(k==3)];
+        v0 = evptx(lf,j0,k1);
+      }
+      if (evptx(lf,ce[4*m+3-2*(k==0)-(k==2)],k1) < v1)
+      { j1 = ce[4*m+3-2*(k==0)-(k==2)];
+        v1 = evptx(lf,j1,k1);
+      }
+    }
+    nc = exvval(lf,g0,j0,2,what,0);
+    nc = exvval(lf,g1,j1,2,what,0);
+    if (nc==1)
+      gg[k] = linear_interp((x[(k>1)]-v0),v1-v0,g0[0],g1[0]);
+    else
+    { hermite2(x[(k>1)]-v0,v1-v0,phi);
+      gg[k] = phi[0]*g0[0]+phi[1]*g1[0]+(phi[2]*g0[1+k1]+phi[3]*g1[1+k1])*(v1-v0);
+      gp[k] = phi[0]*g0[2-k1] + phi[1]*g1[2-k1];
+    }
+  }
+  s = -s;
+  if (nc==1)
+    for (k=0; k<2; k++)
+      s += linear_interp(x[k]-ll[k],ur[k]-ll[k],gg[3-2*k],gg[2-2*k]);
+    else
+    for (k=0; k<2; k++) /* EW NS */
+    { hermite2(x[k]-ll[k],ur[k]-ll[k],phi);
+      s += phi[0]*gg[3-2*k] + phi[1]*gg[2-2*k]
+          +(phi[2]*gp[3-2*k] + phi[3]*gp[2-2*k]) * (ur[k]-ll[k]);
+    }
+  return(s);
+}
+
+double kdtre_int(lf,x,what)
+lfit *lf;
+double *x;
+INT what;
+{ INT d, vc, k, t[20], nt, nc, *ce, j;
+  double *ll, *ur, ff, vv[64][64];
+  d = lf->mi[MDIM];
+  vc = lf->vc;
+  if (d > 6) ERROR(("d too large in kdint"));
+
+  /* descend the tree to find the terminal cell */
+  nt = 0; t[nt] = 0; k = 0;
+  while (lf->s[k] != -1)
+  { nt++;
+    if (nt>=20) { ERROR(("Too many levels in kdint")); return(NOSLN); }
+    k = t[nt] = (x[lf->s[k]] < lf->sv[k]) ? lf->lo[k] : lf->hi[k];
+  }
+
+  ce = &lf->ce[k*vc];
+  ll = evpt(lf,ce[0]);
+  ur = evpt(lf,ce[vc-1]);
+  nc = 0;
+  for (j=0; j<vc; j++) nc = exvval(lf,vv[j],ce[j],d,what,0);
+  ff = rectcell_interp(x,NULL,vv,ll,ur,d,nc);
+
+  if (d==2) ff = blend(lf,ff,x,ll,ur,k*vc,nt,t,what);
+  return(ff);
+}
diff --git a/src/locfit/ev_main.c b/src/locfit/ev_main.c
new file mode 100644
index 0000000..6050cf4
--- /dev/null
+++ b/src/locfit/ev_main.c
@@ -0,0 +1,287 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include "local.h"
+
+void kdtre_guessnv(int* nvm, int* ncm, int* vc, double* dp, int* mi);
+
+INT cvi=-1;
+
+/*
+ * the C version has its own createvar, file src-c/vari.c.
+ * For other versions, use the simple function here.
+ */
+#ifndef CVERSION
+vari *createvar(name,type,n,mode)
+varname name;
+INT type, n, mode;
+{ vari *v;
+printf("creating variable\n");
+  v = (vari *)malloc(sizeof(vari));
+  vdptr(v) = (double *)calloc( vlength(v)=n ,
+              (mode==VDOUBLE) ? sizeof(double) : sizeof(INT));
+  return(v);
+}
+#endif
+
+/*
+ *  guessnv is used to guess the number of vertices etc in the
+ *  evaluation structure.
+ */
+void guessnv(nvm,ncm,vc,dp,mi)
+double *dp;
+int *nvm, *ncm, *vc, *mi;
+{ switch(mi[MEV])
+  { case ETREE:
+      atree_guessnv(nvm,ncm,vc,dp,mi);
+      return;
+    case EPHULL:
+      *nvm = *ncm = mi[MK]*mi[MDIM];
+      *vc = mi[MDIM]+1;
+      return;
+    case EDATA:
+    case ECROS:
+      *nvm = mi[MN];
+      *ncm = *vc = 0;
+      return;
+    case EKDTR:
+    case EKDCE:
+      kdtre_guessnv(nvm,ncm,vc,dp,mi);
+      return;
+    case EXBAR:
+    case ENONE:
+      *nvm = 1;
+      *ncm = *vc = 0;
+      return;
+    case EPRES:
+      /* preset -- leave everything unchanged. */
+      return;
+    default:
+      ERROR(("guessnv: I don't know this evaluation structure."));
+  }
+  return;
+}
+
+/*
+ * trchck checks the working space on the lfit structure 
+ * has space for nvm vertices and ncm cells.
+ */
+int lfit_reqd(d,nvm,ncm)
+INT d, nvm, ncm;
+{ return(nvm*(3*d+8)+ncm);
+}
+int lfit_reqi(nvm,ncm,vc)
+INT nvm, ncm, vc;
+{ return(ncm*vc+3*MAX(ncm,nvm));
+}
+
+void trchck(tr,nvm,ncm,d,p,vc)
+lfit *tr;
+INT nvm, ncm, d, p, vc;
+{ INT rw, *k;
+  double *z;
+
+  tr->xxev = checkvarlen(tr->xxev,d*nvm,"_lfxev",VDOUBLE);
+
+  rw = nvm*(3*d+8)+ncm;
+  tr->tw = checkvarlen(tr->tw,lfit_reqd(d,nvm,ncm),"_lfwork",VDOUBLE);
+  z = (double *)vdptr(tr->tw);
+  tr->coef= z; z += nvm*(d+1);
+  tr->nlx = z; z += nvm*(d+1);
+  tr->t0  = z; z += nvm*(d+1);
+  tr->lik = z; z += 3*nvm;
+  tr->h   = z; z += nvm;
+  tr->deg = z; z += nvm;
+  tr->sv  = z; z += ncm;
+  if (z != (double *)vdptr(tr->tw)+rw)
+    WARN(("trchck: double assign problem"));
+
+  rw = lfit_reqi(nvm,ncm,vc);
+  tr->iw = checkvarlen(tr->iw,rw,"_lfiwork",VINT);
+  k = (INT *)vdptr(tr->iw);
+  tr->ce = k; k += vc*ncm;
+  tr->s  = k; k += MAX(ncm,nvm);
+  tr->lo = k; k += MAX(ncm,nvm);
+  tr->hi = k; k += MAX(ncm,nvm);
+  if (k != (INT *)vdptr(tr->iw)+rw)
+    WARN(("trchck: int assign problem"));
+
+  tr->nvm = nvm; tr->ncm = ncm; tr->mi[MDIM] = d; tr->mi[MP] = p; tr->vc = vc;
+}
+
+#ifdef CVERSION
+void reassign(lf)
+lfit *lf;
+{ INT i, nvm, ncm, vc, d, k, p, *iw;
+  double *tw, *ntw;
+  setvarname(lf->tw,"__lfwork"); /* prevent overwrite */
+  setvarname(lf->iw,"__lfiwork");
+  nvm = lf->nvm; ncm = lf->ncm; vc = lf->vc;
+  tw = (double *)vdptr(lf->tw);
+  iw = (INT *)vdptr(lf->iw);
+  d = lf->mi[MDIM];
+  p = lf->mi[MP];
+  trchck(lf,2*nvm,ncm,d,p,vc);
+  ntw = vdptr(lf->tw);
+/*
+  xev is stored in blocks of d. other matrices by blocks on nvm
+*/
+  k = nvm*d;
+  memcpy(vdptr(lf->xxev),tw,k*sizeof(double));
+  tw += k; ntw += 2*k;
+  for (i=0; i<2*p+2*d+6; i++)
+  { memcpy(ntw,tw,nvm*sizeof(double));
+    tw += nvm; ntw += 2*nvm;
+  }
+  k = ncm;       memcpy(lf->sv,tw,k*sizeof(double));   tw += k;
+
+  k = vc*ncm;       memcpy(lf->ce,iw,k*sizeof(INT)); iw += k;
+  k = MAX(ncm,nvm); memcpy(lf->s,iw,k*sizeof(INT));  iw += k;
+  k = MAX(ncm,nvm); memcpy(lf->lo,iw,k*sizeof(INT)); iw += k;
+  k = MAX(ncm,nvm); memcpy(lf->hi,iw,k*sizeof(INT)); iw += k;
+  deletename("__lfwork");
+  deletename("__lfiwork");
+}
+#endif
+
+void dataf(des,lf)
+design *des;
+lfit *lf;
+{ INT d, i, j, ncm, nv, vc;
+
+  d = lf->mi[MDIM];
+  guessnv(&nv,&ncm,&vc,lf->dp,lf->mi);
+  trchck(lf,nv,0,d,des->p,0);
+
+  for (i=0; i<nv; i++)
+    for (j=0; j<d; j++) evptx(lf,i,j) = datum(lf,j,i);
+  for (i=0; i<nv; i++)
+  { des->vfun(des,lf,i);
+    lf->s[i] = 0;
+  }
+  lf->nv = lf->nvm = nv; lf->nce = 0;
+}
+
+void xbarf(des,lf)
+design *des;
+lfit *lf;
+{ int i, d, nvm, ncm, vc;
+  d = lf->mi[MDIM];
+  guessnv(&nvm,&ncm,&vc,lf->dp,lf->mi);
+  trchck(lf,1,0,d,des->p,0);
+  for (i=0; i<d; i++) evptx(lf,0,i) = lf->pc.xbar[i];
+  des->vfun(des,lf,0);
+  lf->s[0] = 0;
+  lf->nv = 1; lf->nce = 0;
+}
+
+#ifndef GR
+void preset(des,lf)
+design *des;
+lfit *lf;
+{ INT i, nv;
+  double *tmp;
+  nv = lf->nvm;
+  tmp = vdptr(lf->xxev);
+  trchck(lf,nv,0,lf->mi[MDIM],des->p,0);
+  lf->xxev->dpr = tmp;
+  for (i=0; i<nv; i++)
+  { 
+    des->vfun(des,lf,i);
+    lf->s[i] = 0;
+  }
+  lf->nv = nv; lf->nce = 0;
+}
+#endif
+
+void crossf(des,lf)
+design *des;
+lfit *lf;
+{ INT d, i, j, n, nv, ncm, vc;
+
+  n = lf->mi[MN]; d = lf->mi[MDIM];
+  guessnv(&nv,&ncm,&vc,lf->dp,lf->mi);
+  trchck(lf,n,0,d,des->p,0);
+
+  for (i=0; i<n; i++)
+    for (j=0; j<d; j++) evptx(lf,i,j) = datum(lf,j,i);
+  for (cvi=0; cvi<n; cvi++)
+  { lf->s[cvi] = 0;
+    des->vfun(des,lf,cvi);
+  }
+  cvi = -1;
+  lf->nv = n; lf->nce = 0; lf->mi[MN] = n;
+}
+
+void gridf(des,tr)
+design *des;
+lfit *tr;
+{ INT d, i, j, nv, u0, u1, z;
+  nv = 1; d = tr->mi[MDIM];
+  for (i=0; i<d; i++)
+  { if (tr->mg[i]==0)
+      tr->mg[i] = 2+(INT)((tr->fl[i+d]-tr->fl[i])/(tr->sca[i]*tr->dp[DCUT]));
+    nv *= tr->mg[i];
+  }
+  trchck(tr,nv,0,d,des->p,1<<d);
+  for (i=0; i<nv; i++)
+  { z = i;
+    for (j=0; j<d; j++)
+    { u0 = z%tr->mg[j];
+      u1 = tr->mg[j]-1-u0;
+      evptx(tr,i,j) = (tr->mg[j]==1) ? tr->fl[j] :
+                      (u1*tr->fl[j]+u0*tr->fl[j+d])/(tr->mg[j]-1);
+      z = z/tr->mg[j];
+    }
+    tr->s[i] = 0;
+    des->vfun(des,tr,i);
+  }
+  tr->nv = nv; tr->nce = 0;
+}
+
+/*
+  add a new vertex at the midpoint of (x[i0],x[i1]).
+  return the vertex number.
+*/
+INT newsplit(des,lf,i0,i1,pv)
+design *des;
+lfit *lf;
+INT i0, i1, pv;
+{ INT i, nv;
+
+  /* first, check to see if the new point already exists */
+  if (i0>i1) ISWAP(i0,i1);
+  nv = lf->nv;
+  for (i=i1+1; i<nv; i++)
+    if ((lf->lo[i]==i0) && (lf->hi[i]==i1)) return(i);
+  
+  /* the point is new. Now check we have space for the new point. */
+  if (nv==lf->nvm)
+  {
+#ifdef CVERSION
+    reassign(lf);
+#else
+    ERROR(("newsplit: out of vertex space"));
+    return(-1);
+#endif
+  }
+
+  /* compute the new point, and evaluate the fit */
+  lf->lo[nv] = i0;
+  lf->hi[nv] = i1;
+  for (i=0; i<lf->mi[MDIM]; i++)
+    evptx(lf,nv,i) = (evptx(lf,i0,i)+evptx(lf,i1,i))/2;
+  if (pv) /* pseudo vertex */
+  { lf->h[nv] = (lf->h[i0]+lf->h[i1])/2;
+    lf->s[nv] = 1; /* pseudo-vertex */
+  }
+  else /* real vertex */
+  { des->vfun(des,lf,nv);
+    lf->s[nv] = 0;
+  }
+  lf->nv++;
+
+  return(nv);
+}
diff --git a/src/locfit/ev_trian.c b/src/locfit/ev_trian.c
new file mode 100644
index 0000000..c53369b
--- /dev/null
+++ b/src/locfit/ev_trian.c
@@ -0,0 +1,457 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include "local.h"
+
+INT triang_split(tr,ce,le)
+lfit *tr;
+double *le;
+INT *ce;
+{ INT d, i, j, k, nts, vc;
+  double di, dfx[MXDIM];
+  nts = 0; d = tr->mi[MDIM]; vc = d+1;
+  for (i=0; i<d; i++)
+    for (j=i+1; j<=d; j++)
+    { for (k=0; k<d; k++)
+        dfx[k] = evptx(tr,ce[i],k)-evptx(tr,ce[j],k);
+      di = rho(dfx,tr->sca,d,KSPH,NULL);
+      le[i*vc+j] = le[j*vc+i] = di/MIN(tr->h[ce[i]],tr->h[ce[j]]);
+      nts = nts || le[i*vc+j]>tr->dp[DCUT];
+    }
+  return(nts);
+}
+
+void resort(pv,xev,dig)
+double *xev;
+INT *pv, *dig;
+{ double d0, d1, d2;
+  INT i;
+  d0 = d1 = d2 = 0;
+  for (i=0; i<3; i++)
+  { d0 += (xev[3*pv[11]+i]-xev[3*pv[1]+i])*(xev[3*pv[11]+i]-xev[3*pv[1]+i]);
+    d1 += (xev[3*pv[ 7]+i]-xev[3*pv[2]+i])*(xev[3*pv[ 7]+i]-xev[3*pv[2]+i]);
+    d2 += (xev[3*pv[ 6]+i]-xev[3*pv[3]+i])*(xev[3*pv[ 6]+i]-xev[3*pv[3]+i]);
+  }
+  if ((d0<=d1) & (d0<=d2))
+  { dig[0] = pv[1]; dig[1] = pv[11];
+    dig[2] = pv[2]; dig[3] = pv[7];
+    dig[4] = pv[3]; dig[5] = pv[6];
+  }
+  else if (d1<=d2)
+  { dig[0] = pv[2]; dig[1] = pv[7];
+    dig[2] = pv[1]; dig[3] = pv[11];
+    dig[4] = pv[3]; dig[5] = pv[6];
+  }
+  else
+  { dig[0] = pv[3]; dig[1] = pv[6];
+    dig[2] = pv[2]; dig[3] = pv[7];
+    dig[4] = pv[1]; dig[5] = pv[11];
+  }
+}
+
+void triang_grow(des,tr,ce,ct,term)
+design *des;
+lfit *tr;
+INT *ce, *ct, *term;
+{ double le[(1+MXDIM)*(1+MXDIM)], ml;
+  INT pv[(1+MXDIM)*(1+MXDIM)], nce[1+MXDIM], d, i, j, im, jm, vc, dig[6];
+  if (lf_error) return;
+  d = tr->mi[MDIM]; vc = d+1;
+  if (!triang_split(tr,ce,le))
+  { if (ct != NULL)
+    { for (i=0; i<vc; i++) term[*ct*vc+i] = ce[i];
+      (*ct)++;
+    }
+    return;
+  }
+  if (d>3)
+  { ml = 0;
+    for (i=0; i<d; i++)
+      for (j=i+1; j<vc; j++)
+        if (le[i*vc+j]>ml) { ml = le[i*vc+j]; im = i; jm = j; }
+    pv[0] = newsplit(des,tr,ce[im],ce[jm],0);
+    for (i=0; i<vc; i++) nce[i] = ce[i];
+    nce[im] = pv[0]; triang_grow(des,tr,nce,ct,term); nce[im] = ce[im];
+    nce[jm] = pv[0]; triang_grow(des,tr,nce,ct,term);
+    return;
+  }
+
+  for (i=0; i<d; i++)
+    for (j=i+1; j<=d; j++)
+      pv[i*vc+j] = pv[j*vc+i]
+        = newsplit(des,tr,ce[i],ce[j],le[i*vc+j]<=tr->dp[DCUT]);
+  for (i=0; i<=d; i++) /* corners */
+  { for (j=0; j<=d; j++) nce[j] = (j==i) ? ce[i] : pv[i*vc+j];
+    triang_grow(des,tr,nce,ct,term);
+  }
+  
+  if (d==2) /* center for d=2 */
+  { nce[0] = pv[5]; nce[1] = pv[2]; nce[2] = pv[1];
+    triang_grow(des,tr,nce,ct,term);
+  }
+  if (d==3) /* center for d=3 */
+  { resort(pv,vdptr(tr->xxev),dig);
+    nce[0] = dig[0]; nce[1] = dig[1];
+    nce[2] = dig[2]; nce[3] = dig[4]; triang_grow(des,tr,nce,ct,term);
+    nce[2] = dig[5]; nce[3] = dig[3]; triang_grow(des,tr,nce,ct,term);
+    nce[2] = dig[2]; nce[3] = dig[5]; triang_grow(des,tr,nce,ct,term);
+    nce[2] = dig[4]; nce[3] = dig[3]; triang_grow(des,tr,nce,ct,term);
+  }
+  if (d==1) return;
+}
+
+void triang_descend(tr,xa,ce)
+lfit *tr;
+double *xa;
+INT *ce;
+{ double le[(1+MXDIM)*(1+MXDIM)], ml;
+  INT d, vc, i, j, pv[(1+MXDIM)*(1+MXDIM)], im, jm;
+  design *des;
+  des = NULL;
+  if (!triang_split(tr,ce,le)) return;
+  d = tr->mi[MDIM]; vc = d+1;
+
+  if (d>3) /* split longest edge */
+  { ml = 0;
+    for (i=0; i<d; i++)
+      for (j=i+1; j<vc; j++)
+        if (le[i*vc+j]>ml) { ml = le[i*vc+j]; im = i; jm = j; }
+    pv[0] = newsplit(des,tr,ce[im],ce[jm],0);
+    if (xa[im]>xa[jm])
+    { xa[im] -= xa[jm]; xa[jm] *= 2; ce[jm] = pv[0]; }
+    else
+    { xa[jm] -= xa[im]; xa[im] *= 2; ce[im] = pv[0]; }
+    triang_descend(tr,xa,ce);
+    return;
+  }
+
+  for (i=0; i<d; i++)
+    for (j=i+1; j<=d; j++)
+      pv[i*vc+j] = pv[j*vc+i]
+        = newsplit(des,tr,ce[i],ce[j],le[i*d+j]<=tr->dp[DCUT]);
+  for (i=0; i<=d; i++) if (xa[i]>=0.5) /* in corner */
+  { for (j=0; j<=d; j++)
+    { if (i!=j) ce[j] = pv[i*vc+j];
+      xa[j] = 2*xa[j];
+    }
+    xa[i] -= 1;
+    triang_descend(tr,xa,ce);
+    return;
+  }
+  if (d==1) { ERROR(("weights sum to < 1")); }
+  if (d==2) /* center */
+  { ce[0] = pv[5]; xa[0] = 1-2*xa[0];
+    ce[1] = pv[2]; xa[1] = 1-2*xa[1];
+    ce[2] = pv[1]; xa[2] = 1-2*xa[2];
+    triang_descend(tr,xa,ce);
+  }
+  if (d==3) /* center */
+  { double z; INT dig[6];
+    resort(pv,vdptr(tr->xxev),dig);
+    ce[0] = dig[0]; ce[1] = dig[1];
+    xa[0] *= 2; xa[1] *= 2; xa[2] *= 2; xa[3] *= 2;
+    if (xa[0]+xa[2]>=1)
+    { if (xa[0]+xa[3]>=1)
+      { ce[2] = dig[2]; ce[3] = dig[4];
+        z = xa[0];
+        xa[3] += z-1; xa[2] += z-1; xa[0] = xa[1]; xa[1] = 1-z;
+      }
+      else
+      { ce[2] = dig[2]; ce[3] = dig[5];
+        z = xa[3]; xa[3] = xa[1]+xa[2]-1; xa[1] = z;
+        z = xa[2]; xa[2] += xa[0]-1; xa[0] = 1-z;
+    } }
+    else
+    { if (xa[1]+xa[2]>=1)
+      { ce[2] = dig[5]; ce[3] = dig[3];
+        xa[1] = 1-xa[1]; xa[2] -= xa[1]; xa[3] -= xa[1];
+      }
+      else
+      { ce[2] = dig[4]; ce[3] = dig[3];
+        z = xa[3]; xa[3] += xa[1]-1; xa[1] = xa[2];
+        xa[2] = z+xa[0]-1; xa[0] = 1-z;
+    } }
+    triang_descend(tr,xa,ce);
+} }
+
+void covrofdata(lf,V,mn) /* covar of data; mean in mn */
+lfit *lf;
+double *V, *mn;
+{ INT d, i, j, k;
+  double s;
+  s = 0; d = lf->mi[MDIM];
+  for (i=0; i<d*d; i++) V[i] = 0;
+  for (i=0; i<lf->mi[MN]; i++)
+  { s += prwt(lf,i);
+    for (j=0; j<d; j++)
+      for (k=0; k<d; k++)
+        V[j*d+k] += prwt(lf,i)*(datum(lf,j,i)-mn[j])*(datum(lf,k,i)-mn[k]);
+  }
+  for (i=0; i<d*d; i++) V[i] /= s;
+}
+
+INT intri(x,w,xev,xa,d) /* is x in triangle bounded by xd[0..d-1]? */
+double *x, *xev, *xa;
+INT d, *w;
+{ INT i, j;
+  double eps, *r, xd[MXDIM*MXDIM];
+  eps = 1.0e-10;
+  r = &xev[w[d]*d];
+  for (i=0; i<d; i++)
+  { xa[i] = x[i]-r[i];
+    for (j=0; j<d; j++) xd[i*d+j] = xev[w[i]*d+j]-r[j];
+  }
+  solve(xd,xa,d);
+  xa[d] = 1.0;
+  for (i=0; i<d; i++) xa[d] -= xa[i];
+  for (i=0; i<=d; i++) if ((xa[i]<-eps) | (xa[i]>1+eps)) return(0);
+  return(1);
+}
+
+void triang_start(des,tr) /* Triangulation with polyhedral start */
+design *des;
+lfit *tr;
+{ INT i, j, k, n, d, nc, nvm, ncm, vc, *ce, ed[1+MXDIM];
+  double V[MXDIM*MXDIM], P[MXDIM*MXDIM], sigma, z[MXDIM], xa[1+MXDIM], *xev;
+  xev = vdptr(tr->xxev);
+  d = tr->mi[MDIM]; n = tr->mi[MN]; tr->nv = nc = 0;
+
+  guessnv(&nvm,&ncm,&vc,tr->dp,tr->mi);
+  trchck(tr,nvm,ncm,d,des->p,vc);
+
+  ce = tr->ce;
+  for (j=0; j<d; j++) xev[j] = tr->pc.xbar[j];
+  tr->nv = 1;
+  covrofdata(tr,V,tr->pc.xbar); /* fix this with scaling */
+  eig_dec(V,P,d);
+
+  for (i=0; i<d; i++) /* add vertices +- 2sigma*eigenvect */
+  { sigma = sqrt(V[i*(d+1)]);
+    for (j=0; j<d; j++)
+      xev[tr->nv*d+j] = xev[j]-2*sigma*P[j*d+i];
+    tr->nv++;
+    for (j=0; j<d; j++)
+      xev[tr->nv*d+j] = xev[j]+2*sigma*P[j*d+i];
+    tr->nv++;
+  }
+
+  for (i=0; i<n; i++) /* is point i inside? */
+  { ed[0] = 0;
+    for (j=0; j<d; j++)
+    { z[j] = 0;
+      for (k=0; k<d; k++) z[j] += P[k*d+j]*(datum(tr,k,i)-xev[k]);
+      ed[j+1] = 2*j+1+(z[j]>0);
+      for (k=0; k<d; k++) z[j] = datum(tr,j,i);
+    }
+    k = intri(z,ed,xev,xa,d);
+    if (xa[0]<0)
+    { for (j=1; j<=d; j++)
+        for (k=0; k<d; k++)
+          xev[ed[j]*d+k] = xa[0]*xev[k]+(1-xa[0])*xev[ed[j]*d+k];
+    }
+  }
+
+  nc = 1<<d; /* create initial cells */
+  for (i=0; i<nc; i++)
+  { ce[i*vc] = 0; k = i;
+    for (j=0; j<d; j++)
+    { ce[i*vc+j+1] = 2*j+(k%2)+1;
+      k>>=1;
+    }
+  }
+
+  for (i=0; i<tr->nv; i++)
+  { des->vfun(des,tr,i);
+    if (lf_error) return;
+    tr->s[i] = 0;
+  }
+  for (i=0; i<nc; i++)
+  { for (j=0; j<=d; j++) ed[j] = tr->ce[i*vc+j];
+    triang_grow(des,tr,&tr->ce[i*vc],(INT *)NULL,(INT *)NULL);
+  }
+  tr->nce = nc;
+}
+
+double triang_cubicint(v,vv,w,d,nc,xxa)
+double *v, *vv, *xxa;
+INT d, *w, nc;
+{ double sa, lb, *vert0, *vert1, *vals0, *vals1, deriv0, deriv1;
+  INT i, j, k;
+  if (nc==1) /* linear interpolate */
+  { sa = 0;
+    for (i=0; i<=d; i++) sa += xxa[i]*vv[i];
+    return(sa);
+  }
+  sa = 1.0;
+    vals0 = NULL;
+  for (j=d; j>0; j--)  /* eliminate v[w[j]] */
+  { lb = xxa[j]/sa;
+    for (k=0; k<j; k++) /* Interpolate edge v[w[k]],v[w[j]] */
+    { vert0 = &v[w[k]*d];
+      vert1 = &v[w[j]*d];
+      vals0 = &vv[k*nc];
+      vals1 = &vv[j*nc];
+      deriv0 = deriv1 = 0;
+      for (i=0; i<d; i++)
+      { deriv0 += (vert1[i]-vert0[i])*vals0[i+1];
+        deriv1 += (vert1[i]-vert0[i])*vals1[i+1];
+      }
+      vals0[0] = cubic_interp(lb,vals0[0],vals1[0],deriv0,deriv1);
+      for (i=1; i<=d; i++)
+        vals0[i] = (1-lb)*((1-lb)*vals0[i]+lb*vals1[i]);
+    }
+    sa -= xxa[j];
+    if (sa<=0) j = 0;
+  }
+    if (vals0)
+      return(vals0[0]);
+    else
+    {
+        fprintf(stderr, "Warning: vals0 is undefined!\n");
+        return 0.0;
+    }
+}
+
+double triang_clotoch(xev,vv,ce,p,xxa)
+double *xev, *vv, *xxa;
+INT *ce, p;
+{ double cfo[3], cfe[3], cg[9], *va, *vb, *vc,
+    l0, nm[3], na, nb, nc, *xl, *xr, *xz, d0, d1, lb, dlt, gam;
+  INT i, w[3], cfl, cfr;
+  if (p==1)
+    return(xxa[0]*vv[0]+xxa[1]*vv[1]+xxa[2]*vv[2]);
+  if (xxa[2]<=MIN(xxa[0],xxa[1]))
+  { va = &xev[2*ce[0]]; vb = &xev[2*ce[1]]; vc = &xev[2*ce[2]];
+    w[0] = 0; w[1] = 3; w[2] = 6;
+  }
+  else
+  if (xxa[1]<xxa[0])
+  { w[0] = 0; w[1] = 6; w[2] = 3;
+    va = &xev[2*ce[0]]; vb = &xev[2*ce[2]]; vc = &xev[2*ce[1]];
+    lb = xxa[1]; xxa[1] = xxa[2]; xxa[2] = lb;
+  }
+  else
+  { w[0] = 6; w[1] = 3; w[2] = 0;
+    va = &xev[2*ce[2]]; vb = &xev[2*ce[1]]; vc = &xev[2*ce[0]];
+    lb = xxa[0]; xxa[0] = xxa[2]; xxa[2] = lb;
+  }
+  
+/* set cg to values and derivatives on standard triangle */
+  for (i=0; i<3; i++)
+  { cg[3*i] = vv[w[i]];
+    cg[3*i+1] = ((vb[0]-va[0])*vv[w[i]+1]
+                +(vb[1]-va[1])*vv[w[i]+2])/2;  /* df/dx */
+    cg[3*i+2] = ((2*vc[0]-vb[0]-va[0])*vv[w[i]+1]
+                +(2*vc[1]-vb[1]-va[1])*vv[w[i]+2])/2.0; /* sqrt{3} df/dy */
+  }
+  dlt = (vb[0]-va[0])*(vc[1]-va[1])-(vc[0]-va[0])*(vb[1]-va[1]);
+  /* Twice area; +ve if abc antic.wise  -ve is abc c.wise */
+  cfo[0] = (cg[0]+cg[3]+cg[6])/3;
+  cfo[1] = (2*cg[0]-cg[3]-cg[6])/4;
+  cfo[2] = (2*cg[3]-cg[0]-cg[6])/4;
+  na = -cg[1]+cg[2];  /* perp. deriv, rel. length 2 */
+  nb = -cg[4]-cg[5];
+  nc = 2*cg[7];
+  cfo[1] += (nb-nc)/16;
+  cfo[2] += (nc-na)/16;
+  na = -cg[1]-cg[2]/3.0;  /* derivatives back to origin */
+  nb =  cg[4]-cg[5]/3.0;
+  nc =        cg[8]/1.5;
+  cfo[0] -= (na+nb+nc)*7/54;
+  cfo[1] += 13*(nb+nc-2*na)/144;
+  cfo[2] += 13*(na+nc-2*nb)/144;
+  for (i=0; i<3; i++)
+  { /* Outward normals by linear interpolation on original triangle.
+       Convert to outward normals on standard triangle.
+       Actually, computed to opposite corner */
+    switch(i)
+    { case 0: xl = vc; xr = vb; xz = va; cfl = w[2]; cfr = w[1];
+              break;
+      case 1: xl = va; xr = vc; xz = vb; cfl = w[0]; cfr = w[2];
+              break;
+      case 2: xl = vb; xr = va; xz = vc; cfl = w[1]; cfr = w[0];
+              break;
+    }
+    na = xr[0]-xl[0]; nb = xr[1]-xl[1];
+    lb = na*na+nb*nb;
+    d0 = 1.5*(vv[cfr]-vv[cfl]) - 0.25*(na*(vv[cfl+1]+vv[cfr+1])
+         +nb*(vv[cfl+2]+vv[cfr+2]));
+    d1 = 0.5*( na*(vv[cfl+2]+vv[cfr+2])-nb*(vv[cfl+1]+vv[cfr+1]) );
+    l0 = (xz[0]-xl[0])*na+(xz[1]-xl[1])*nb-lb/2;
+    nm[i] = (d1*dlt-l0*d0)/lb;
+  }
+  cfo[0] -= (nm[0]+nm[1]+nm[2])*4/81;
+  cfo[1] += (2*nm[0]-nm[1]-nm[2])/27;
+  cfo[2] += (2*nm[1]-nm[0]-nm[2])/27;
+
+  gam = xxa[0]+xxa[1]-2*xxa[2];
+  if (gam==0) return(cfo[0]);
+  lb = (xxa[0]-xxa[2])/gam;
+  d0 = -2*cg[4]; d1 = -2*cg[1];
+  cfe[0] = cubic_interp(lb,cg[3],cg[0],d0,d1);
+  cfe[1] = cubintd(lb,cg[3],cg[0],d0,d1);
+  cfe[2] = -(1-lb)*(1-2*lb)*cg[5] + 4*lb*(1-lb)*nm[2] - lb*(2*lb-1)*cg[2];
+  d0 = 2*(lb*cfo[1]+(1-lb)*cfo[2]);
+  d1 = (lb-0.5)*cfe[1]+cfe[2]/3.0;
+  return(cubic_interp(gam,cfo[0],cfe[0],d0,d1));
+}
+
+INT triang_getvertexvals(lf,vv,i,what)
+lfit *lf;
+double *vv;
+INT i, what;
+{ double dx, P, le, vl[1+MXDIM], vh[1+MXDIM];
+  INT d, il, ih, j, nc;
+  d = lf->mi[MDIM];
+  if (lf->s[i]==0) return(exvval(lf,vv,i,d,what,0));
+
+  il = lf->lo[i]; nc = triang_getvertexvals(lf,vl,il,what);
+  ih = lf->hi[i]; nc = triang_getvertexvals(lf,vh,ih,what);
+  vv[0] = (vl[0]+vh[0])/2;
+  if (nc==1) return(nc);
+  P = 1.5*(vh[0]-vl[0]);
+  le = 0.0;
+  for (j=0; j<d; j++)
+  { dx = evptx(lf,ih,j)-evptx(lf,il,j);
+    vv[0] += dx*(vl[j+1]-vh[j+1])/8;
+    vv[j+1] = (vl[j+1]+vh[j+1])/2;
+    P -= 1.5*dx*vv[j+1];
+    le += dx*dx;
+  }
+  for (j=0; j<d; j++)
+    vv[j+1] += P*(evptx(lf,ih,j)-evptx(lf,il,j))/le;
+  return(nc);
+}
+
+double triang_int(tr,x,what)
+lfit *tr;
+double *x;
+INT what;
+{ INT d, vc, i, j, k, *ce, nc, nce[1+MXDIM];
+  double xa[1+MXDIM], vv[(1+MXDIM)*(1+MXDIM)], lb;
+  d = tr->mi[MDIM]; vc = d+1;
+  ce = tr->ce;
+  i = 0;
+  while ((i<tr->nce) && (!intri(x,&ce[i*vc],vdptr(tr->xxev),xa,d))) i++;
+  if (i==tr->nce) return(NOSLN);
+  i *= vc;
+  for (j=0; j<vc; j++) nce[j] = ce[i+j];
+  triang_descend(tr,xa,nce);
+
+  /* order the vertices -- needed for asymmetric interptr */
+  do
+  { k=0;
+    for (i=0; i<d; i++)
+      if (nce[i]>nce[i+1])
+      { j=nce[i]; nce[i]=nce[i+1]; nce[i+1]=j; k=1;
+        lb = xa[i]; xa[i] = xa[i+1]; xa[i+1] = lb;
+      }
+  } while(k);
+  nc = 0;
+  for (i=0; i<vc; i++)
+    nc =  triang_getvertexvals(tr,&vv[i*nc],nce[i],what);
+  return((d==2) ? triang_clotoch(vdptr(tr->xxev),vv,nce,nc,xa) :
+                 triang_cubicint(vdptr(tr->xxev),vv,nce,d,nc,xa));
+}
diff --git a/src/locfit/family.c b/src/locfit/family.c
new file mode 100644
index 0000000..8de7a8b
--- /dev/null
+++ b/src/locfit/family.c
@@ -0,0 +1,607 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include "local.h"
+
+#define HUBERC 2.0
+
+extern double rs, log();
+
+INT defaultlink(link,family)
+INT link, family;
+{ if (link==LDEFAU)
+    switch(family&63)
+    { case TDEN:
+      case TRAT:
+      case THAZ:
+      case TGAMM:
+      case TGEOM:
+      case TPROB:
+      case TPOIS: return(LLOG);
+      case TCIRC:
+      case TGAUS:
+      case TCAUC:
+      case TROBT: return(LIDENT);
+      case TRBIN:
+      case TLOGT: return(LLOGIT);
+    }
+  if (link==LCANON)
+    switch(family&63)
+    { case TDEN:
+      case TRAT:
+      case THAZ:
+      case TPROB:
+      case TPOIS: return(LLOG);
+      case TGEOM:
+        WARN(("Canonical link unavaialable for geometric family; using inverse"));
+      case TGAMM: return(LINVER);
+      case TCIRC:
+      case TGAUS:
+      case TCAUC:
+      case TROBT: return(LIDENT);
+      case TRBIN:
+      case TLOGT: return(LLOGIT);
+    }
+  return(link);
+}
+
+INT validlinks(link,family)
+INT link, family;
+{ switch(family&63)
+  { case TDEN:
+    case TRAT:
+    case THAZ:
+      return((link==LLOG) | (link==LIDENT));
+    case TGAUS:
+      return((link==LIDENT) | (link==LLOG) | (link==LLOGIT));
+    case TROBT:
+    case TCAUC:
+    case TCIRC:
+      return(link==LIDENT);
+    case TLOGT:
+      return((link==LLOGIT) | (link==LIDENT) | (link==LASIN));
+    case TRBIN:
+      return(link==LLOGIT);
+    case TGAMM:
+      return((link==LLOG) | (link==LINVER) | (link==LIDENT));
+    case TGEOM:
+      return((link==LLOG) | (link==LIDENT));
+    case TPOIS:
+    case TPROB:
+      return((link==LLOG) | (link==LSQRT) | (link==LIDENT));
+  }
+  ERROR(("Unknown family %d in validlinks",family));
+  return(0);
+}
+
+INT famdens(mean,th,link,res,cens,w)
+double mean, th, *res, w;
+INT link, cens;
+{ if (cens)
+    res[ZLIK] = res[ZDLL] = res[ZDDLL] = 0.0;
+  else
+  { res[ZLIK] = w*th;
+    res[ZDLL] = res[ZDDLL] = w;
+  }
+  return(LF_OK);
+}
+
+INT famgaus(y,mean,th,link,res,cens,w)
+double y, mean, th, *res, w;
+INT link, cens;
+{ double z, pz, dp;
+  if (link==LINIT)
+  { res[ZDLL] = w*y;
+    return(LF_OK);
+  }
+  z = y-mean;
+  if (cens)
+  { if (link!=LIDENT)
+    { ERROR(("Link invalid for censored Gaussian family"));
+      return(LF_LNK);
+    }
+    pz = pnorm(-z,0.0,1.0);
+    dp = ((z>6) ? ptail(-z) : exp(-z*z/2)/pz)/2.5066283;
+    res[ZLIK] = w*log(pz);
+    res[ZDLL] = w*dp;
+    res[ZDDLL]= w*dp*(dp-z);
+    return(LF_OK);
+  }
+  res[ZLIK] = -w*z*z/2; 
+  switch(link)
+  { case LIDENT:
+      res[ZDLL] = w*z;
+      res[ZDDLL]= w;
+      break;
+    case LLOG:
+      res[ZDLL] = w*z*mean;
+      res[ZDDLL]= w*mean*mean;
+      break;
+    case LLOGIT:
+      res[ZDLL] = w*z*mean*(1-mean);
+      res[ZDDLL]= w*mean*mean*(1-mean)*(1-mean);
+      break;
+    default:
+      ERROR(("Invalid link for Gaussian family"));
+      return(LF_LNK);
+  }
+  return(LF_OK);
+}
+
+INT famrobu(y,mean,th,link,res,cens,w,rs)
+double y, mean, th, *res, w, rs;
+INT link, cens;
+{ double z, sw;
+  if (link==LINIT)
+  { res[ZDLL] = w*y;
+    return(LF_OK);
+  }
+  sw = (w==1.0) ? 1.0 : sqrt(w); /* don't want unnecess. sqrt! */
+  z = sw*(y-mean)/rs;
+  res[ZLIK] = (fabs(z)<HUBERC) ? -z*z/2 : HUBERC*(HUBERC/2.0-fabs(z));
+  if (z< -HUBERC)
+  { res[ZDLL] = -sw*HUBERC/rs;
+    res[ZDDLL]= 0.0;
+    return(LF_OK);
+  }
+  if (z> HUBERC)
+  { res[ZDLL] = sw*HUBERC/rs;
+    res[ZDDLL]= 0.0;
+    return(LF_OK);
+  }
+  res[ZDLL] =  sw*z/rs;
+  res[ZDDLL] = w/(rs*rs);
+  return(LF_OK);
+}
+
+INT famcauc(y,p,th,link,res,cens,w,rs)
+double y, p, th, *res, w, rs;
+INT link, cens;
+{ double z;
+  if (link!=LIDENT)
+  { ERROR(("Invalid link in famcauc"));
+    return(LF_LNK);
+  }
+  z = w*(y-th)/rs;
+  res[ZLIK] = -log(1+z*z);
+  res[ZDLL] = 2*w*z/(rs*(1+z*z));
+  res[ZDDLL] = 2*w*w*(1-z*z)/(rs*rs*(1+z*z)*(1+z*z));
+  return(LF_OK);
+}
+
+INT famrbin(y,p,th,link,res,cens,w)
+double y, p, th, *res, w;
+INT link, cens;
+{ double s2y;
+  if (link==LINIT)
+  { res[ZDLL] = y;
+    return(LF_OK);
+  }
+  if ((y<0) | (y>w)) /* goon observation; delete it */
+  { res[ZLIK] = res[ZDLL] = res[ZDDLL] = 0.0;
+    return(LF_OK);
+  }
+  res[ZLIK] = (th<0) ? th*y-w*log(1+exp(th)) : th*(y-w)-w*log(1+exp(-th));
+  if (y>0) res[ZLIK] -= y*log(y/w);
+  if (y<w) res[ZLIK] -= (w-y)*log(1-y/w);
+  res[ZDLL] = (y-w*p);
+  res[ZDDLL]= w*p*(1-p);
+  if (-res[ZLIK]>HUBERC*HUBERC/2.0)
+  { s2y = sqrt(-2*res[ZLIK]);
+    res[ZLIK] = HUBERC*(HUBERC/2.0-s2y);
+    res[ZDLL] *= HUBERC/s2y;
+    res[ZDDLL] = HUBERC/s2y*(res[ZDDLL]-1/(s2y*s2y)*w*p*(1-p));
+  }
+  return(LF_OK);
+}
+
+INT fambino(y,p,th,link,res,cens,w)
+double y, p, th, *res, w;
+INT link, cens;
+{ double wp;
+  if (link==LINIT)
+  { if (y<0) y = 0;
+    if (y>w) y = w;
+    res[ZDLL] = y;
+    return(LF_OK);
+  }
+  wp = w*p;
+  if (link==LIDENT)
+  { if ((p<=0) && (y>0)) return(LF_BADP);
+    if ((p>=1) && (y<w)) return(LF_BADP);
+    res[ZLIK] = res[ZDLL] = res[ZDDLL] = 0.0;
+    if (y>0)
+    { res[ZLIK] += y*log(wp/y);
+      res[ZDLL] += y/p;
+      res[ZDDLL]+= y/(p*p);
+    }
+    if (y<w)
+    { res[ZLIK] += (w-y)*log((w-wp)/(w-y));
+      res[ZDLL] -= (w-y)/(1-p);
+      res[ZDDLL]+= (w-y)/SQR(1-p);
+    }
+    return(LF_OK);
+  }
+  if (link==LLOGIT)
+  { if ((y<0) | (y>w)) /* goon observation; delete it */
+    { res[ZLIK] = res[ZDLL] = res[ZDDLL] = 0.0;
+      return(LF_OK);
+    }
+    res[ZLIK] = (th<0) ? th*y-w*log(1+exp(th)) : th*(y-w)-w*log(1+exp(-th));
+    if (y>0) res[ZLIK] -= y*log(y/w);
+    if (y<w) res[ZLIK] -= (w-y)*log(1-y/w);
+    res[ZDLL] = (y-wp);
+    res[ZDDLL]= wp*(1-p);
+    return(LF_OK);
+  }
+  if (link==LASIN)
+  { if ((p<=0) && (y>0)) return(LF_BADP);
+    if ((p>=1) && (y<w)) return(LF_BADP);
+    if ((th<0) | (th>PI/2)) return(LF_BADP);
+    res[ZDLL] = res[ZDDLL] = res[ZLIK] = 0;
+    if (y>0)
+    { res[ZDLL] += 2*y*sqrt((1-p)/p);
+      res[ZLIK] += y*log(wp/y);
+    }
+    if (y<w)
+    { res[ZDLL] -= 2*(w-y)*sqrt(p/(1-p));
+      res[ZLIK] += (w-y)*log((w-wp)/(w-y));
+    }
+    res[ZDDLL] = 4*w;
+    return(LF_OK);
+  }
+  ERROR(("link %d invalid for binomial family",link));
+  return(LF_LNK);
+}
+
+INT fampois(y,mean,th,link,res,cens,w)
+double y, mean, th, *res, w;
+INT link, cens;
+{ double wmu, pt, dp, dq;
+  if (link==LINIT)
+  { res[ZDLL] = MAX(y,0.0);
+    return(LF_OK);
+  }
+  wmu = w*mean;
+  if (cens)
+  { if (y<=0)
+    { res[ZLIK] = res[ZDLL] = res[ZDDLL] = 0.0;
+      return(LF_OK);
+    }
+    pt = igamma(wmu,y);
+    dp = exp((y-1)*log(wmu)-wmu-LGAMMA(y))/pt;
+    dq = dp*((y-1)/wmu-1);
+    res[ZLIK] = log(pt);
+    if (link==LLOG)
+    { res[ZDLL] = dp*wmu;
+      res[ZDDLL]= -(dq-dp*dp)*wmu*wmu-dp*wmu;
+      return(LF_OK);
+    }
+    if (link==LIDENT)
+    { res[ZDLL] = dp*w;
+      res[ZDDLL]= -(dq-dp*dp)*w*w;
+      return(LF_OK);
+    }
+    if (link==LSQRT)
+    { res[ZDLL] = dp*2*w*th;
+      res[ZDDLL]= -(dq-dp*dp)*(4*w*w*mean)-2*dp*w;
+      return(LF_OK);
+  } }
+  if (link==LLOG)
+  { if (y<0) /* goon observation - delete it */
+    { res[ZLIK] = res[ZDLL] = res[ZDDLL] = 0;
+      return(LF_OK);
+    }
+    res[ZLIK] = res[ZDLL] = y-wmu;
+    if (y>0) res[ZLIK] += y*(th-log(y/w));
+    res[ZDDLL] = wmu;
+    return(LF_OK);
+  }
+  if (link==LIDENT)
+  { if ((mean<=0) && (y>0)) return(LF_BADP);
+    res[ZLIK] = y-wmu;
+    res[ZDLL] = -w;
+    res[ZDDLL] = 0;
+    if (y>0)
+    { res[ZLIK] += y*log(wmu/y);
+      res[ZDLL] += y/mean;
+      res[ZDDLL]= y/(mean*mean);
+    }
+    return(LF_OK);
+  }
+  if (link==LSQRT)
+  { if ((mean<=0) && (y>0)) return(LF_BADP);
+    res[ZLIK] = y-wmu;
+    res[ZDLL] = -2*w*th;
+    res[ZDDLL]= 2*w;
+    if (y>0)
+    { res[ZLIK] += y*log(wmu/y);
+      res[ZDLL] += 2*y/th;
+      res[ZDDLL]+= 2*y/mean;
+    }
+    return(LF_OK);
+  }
+  ERROR(("link %d invalid for Poisson family",link));
+  return(LF_LNK);
+}
+
+INT famgamm(y,mean,th,link,res,cens,w)
+double y, mean, th, *res, w;
+INT link, cens;
+{ double pt, dg;
+  if (link==LINIT)
+  { res[ZDLL] = MAX(y,0.0);
+    return(LF_OK);
+  }
+  if ((mean<=0) & (y>0)) return(LF_BADP);
+  if (cens)
+  { if (y<=0)
+    { res[ZLIK] = res[ZDLL] = res[ZDDLL] = 0.0;
+      return(LF_OK);
+    }
+    if (link==LLOG)
+    { pt = 1-igamma(y/mean,w);
+      dg = exp((w-1)*log(y/mean)-y/mean-LGAMMA(w));
+      res[ZLIK] = log(pt);
+      res[ZDLL] = y*dg/(mean*pt);
+      res[ZDDLL]= dg*(w*y/mean-y*y/(mean*mean))/pt+SQR(res[ZDLL]);
+      return(LF_OK);
+    }
+    if (link==LINVER)
+    { pt = 1-igamma(th*y,w);
+      dg = exp((w-1)*log(th*y)-th*y-LGAMMA(w));
+      res[ZLIK] = log(pt);
+      res[ZDLL] = -y*dg/pt;
+      res[ZDDLL]= dg*y*((w-1)*mean-y)/pt+SQR(res[ZDLL]);
+      return(LF_OK);
+    }
+  }
+  else
+  { if (y<0) WARN(("Negative Gamma observation"));
+    if (link==LLOG)
+    { res[ZLIK] = -y/mean+w*(1-th);
+      if (y>0) res[ZLIK] += w*log(y/w);
+      res[ZDLL] = y/mean-w;
+      res[ZDDLL]= y/mean;
+      return(LF_OK);
+    }
+    if (link==LINVER)
+    { res[ZLIK] = -y/mean+w-w*log(mean);
+      if (y>0) res[ZLIK] += w*log(y/w);
+      res[ZDLL] = -y+w*mean;
+      res[ZDDLL]= w*mean*mean;
+      return(LF_OK);
+    }
+    if (link==LIDENT)
+    { res[ZLIK] = -y/mean+w-w*log(mean);
+      if (y>0) res[ZLIK] += w*log(y/w);
+      res[ZDLL] = (y-mean)/(mean*mean);
+      res[ZDDLL]= w/(mean*mean);
+      return(LF_OK);
+    }
+  }
+  ERROR(("link %d invalid for Gamma family",link));
+  return(LF_LNK);
+}
+
+INT famgeom(y,mean,th,link,res,cens,w)
+double y, mean, th, *res, w;
+INT link, cens;
+{ double p, pt, dp, dq;
+  if (link==LINIT)
+  { res[ZDLL] = MAX(y,0.0);
+    return(LF_OK);
+  }
+  p = 1/(1+mean);
+  if (cens) /* censored observation */
+  { if (y<=0)
+    { res[ZLIK] = res[ZDLL] = res[ZDDLL] = 0;
+      return(LF_OK);
+    }
+    pt = 1-ibeta(p,w,y);
+    dp = -exp(LGAMMA(w+y)-LGAMMA(w)-LGAMMA(y)+(y-1)*th+(w+y-2)*log(p))/pt;
+    dq = ((w-1)/p-(y-1)/(1-p))*dp;
+    res[ZLIK] = log(pt);
+    res[ZDLL] = -dp*p*(1-p);
+    res[ZDDLL]= (dq-dp*dp)*p*p*(1-p)*(1-p)+dp*(1-2*p)*p*(1-p);
+    res[ZDDLL]= -res[ZDDLL];
+    return(LF_OK);
+  }
+  else
+  { res[ZLIK] = (y+w)*log((y/w+1)/(mean+1));
+    if (y>0) res[ZLIK] += y*log(w*mean/y);
+    if (link==LLOG)
+    { res[ZDLL] = (y-w*mean)*p;
+      res[ZDDLL]= (y+w)*p*(1-p);
+      return(LF_OK);
+    }
+    if (link==LIDENT)
+    { res[ZDLL] = (y-w*mean)/(mean*(1+mean));
+      res[ZDDLL]= w/(mean*(1+mean));
+      return(LF_OK);
+    }
+  }
+  ERROR(("link %d invalid for geometric family",link));
+  return(LF_LNK);
+}
+
+INT famweib(y,mean,th,link,res,cens,w)
+double y, mean, th, *res, w;
+INT link, cens;
+{ double yy;
+  yy = pow(y,w);
+  if (link==LINIT)
+  { res[ZDLL] = MAX(yy,0.0);
+    return(LF_OK);
+  }
+  if (cens)
+  { res[ZLIK] = -yy/mean;
+    res[ZDLL] = res[ZDDLL] = yy/mean;
+    return(LF_OK);
+  }
+  res[ZLIK] = 1-yy/mean-th;
+  if (yy>0) res[ZLIK] += log(w*yy);
+  res[ZDLL] = -1+yy/mean;
+  res[ZDDLL]= yy/mean;
+  return(LF_OK);
+}
+
+INT famcirc(y,mean,th,link,res,cens,w)
+double y, mean, th, *res, w;
+INT link, cens;
+{ if (link==LINIT)
+  { res[ZDLL] = w*sin(y);
+    res[ZLIK] = w*cos(y);
+    return(LF_OK);
+  }
+  res[ZDLL] = w*sin(y-mean);
+  res[ZDDLL]= w*cos(y-mean);
+  res[ZLIK] = res[ZDDLL]-w;
+  return(LF_OK);
+}
+
+void robustify(res,rs)
+double *res, rs;
+{ double sc, z;
+  sc = rs*HUBERC;
+  if (res[ZLIK] > -sc*sc/2) return;
+  z = sqrt(-2*res[ZLIK]);
+  res[ZDDLL]= -sc*res[ZDLL]*res[ZDLL]/(z*z*z)+sc*res[ZDDLL]/z;
+  res[ZDLL]*= sc/z;
+  res[ZLIK] = sc*sc/2-sc*z;
+}
+
+double lf_link(y,lin)
+double y;
+INT lin;
+{ switch(lin)
+  { case LIDENT: return(y);
+    case LLOG:   return(log(y));
+    case LLOGIT: return(logit(y));
+    case LINVER: return(1/y);
+    case LSQRT:  return(sqrt(fabs(y)));
+    case LASIN:  return(asin(sqrt(y)));
+  }
+  ERROR(("link: unknown link %d",lin));
+  return(0.0);
+}
+
+double invlink(th,lin)
+double th;
+INT lin;
+{ switch(lin)
+  { case LIDENT: return(th);
+    case LLOG:   return(lf_exp(th));
+    case LLOGIT: return(expit(th));
+    case LINVER: return(1/th);
+    case LSQRT:  return(th*fabs(th));
+    case LASIN:  return(sin(th)*sin(th));
+    case LINIT:  return(0.0);
+  }
+  ERROR(("invlink: unknown link %d",lin));
+  return(0.0);
+}
+
+INT links(th,y,fam,lin,res,cd,w,rs) /* the link and various related functions */
+double th, y, *res, w, cd, rs;
+INT fam, lin;
+{ double mean;
+  INT c, link, st;
+  c = (INT)cd; link = (INT)lin;
+
+  mean = res[ZMEAN] = invlink(th,lin);
+  if (lf_error) return(LF_LNK);
+
+  switch(fam&63)
+  { case THAZ:
+    case TDEN:
+    case TRAT: return(famdens(mean,th,link,res,c,w));
+    case TGAUS: st = famgaus(y,mean,th,link,res,c,w);
+                break;
+    case TLOGT: st = fambino(y,mean,th,link,res,c,w);
+                break;
+    case TRBIN: return(famrbin(y,mean,th,link,res,c,w));
+    case TPROB:
+    case TPOIS: st = fampois(y,mean,th,link,res,c,w);
+                break;
+    case TGAMM: st = famgamm(y,mean,th,link,res,c,w);
+                break;
+    case TGEOM: st = famgeom(y,mean,th,link,res,c,w);
+                break;
+    case TWEIB: return(famweib(y,mean,th,link,res,c,w));
+    case TCIRC: st = famcirc(y,mean,th,link,res,c,w);
+                break;
+    case TROBT: return(famrobu(y,mean,th,link,res,c,w,rs));
+    case TCAUC: return(famcauc(y,mean,th,link,res,c,w,rs));
+    default:
+      ERROR(("links: invalid family %d",fam));
+      return(LF_FAM);
+  }
+  if (st!=LF_OK) return(st);
+  if (link==LINIT) return(st);
+  if ((fam&128)==128) robustify(res,rs);
+  return(st);
+}
+
+/*
+  stdlinks is a version of links when family, link, response e.t.c
+  all come from the standard places.
+*/
+INT stdlinks(res,lf,i,th,rs)
+lfit *lf;
+double th, rs, *res;
+INT i;
+{ return(links(th,resp(lf,i),lf->mi[MTG],lf->mi[MLINK],res,cens(lf,i),prwt(lf,i),rs));
+}
+
+/*
+ *  functions used in variance, skewness, kurtosis calculations
+ *  in scb corrections.
+ */
+
+double b2(th,tg,w)
+double th, w;
+INT tg;
+{ double y;
+  switch(tg&63)
+  { case TGAUS: return(w);
+    case TPOIS: return(w*lf_exp(th));
+    case TLOGT:
+      y = expit(th);
+      return(w*y*(1-y));
+  }
+  ERROR(("b2: invalid family %d",tg));
+  return(0.0);
+}
+
+double b3(th,tg,w)
+double th, w;
+INT tg;
+{ double y;
+  switch(tg&63)
+  { case TGAUS: return(0.0);
+    case TPOIS: return(w*lf_exp(th));
+    case TLOGT:
+      y = expit(th);
+      return(w*y*(1-y)*(1-2*y));
+  }
+  ERROR(("b3: invalid family %d",tg));
+  return(0.0);
+}
+
+double b4(th,tg,w)
+double th, w;
+INT tg;
+{ double y;
+  switch(tg&63)
+  { case TGAUS: return(0.0);
+    case TPOIS: return(w*lf_exp(th));
+    case TLOGT:
+      y = expit(th); y = y*(1-y);
+      return(w*y*(1-6*y));
+  }
+  ERROR(("b4: invalid family %d",tg));
+  return(0.0);
+}
diff --git a/src/locfit/fitted.c b/src/locfit/fitted.c
new file mode 100644
index 0000000..0964a02
--- /dev/null
+++ b/src/locfit/fitted.c
@@ -0,0 +1,154 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ */
+
+/*
+  Functions for computing residuals and fitted values from
+  the locfit object.
+
+  fitted(lf,des,fit,what,cv,ty) computes fitted values from the
+    fit structure in lf. 
+  resid(y,c,w,th,mi,ty) converts fitted values to residuals
+  cfitted(v,ty) is CVERSION front end, interpreting command
+    line arguments and computing th.
+  vfitted(ty) for use by arithmetic interpreter.
+*/
+
+#include "local.h"
+
+double resid(y,w,th,mi,ty,res)
+INT *mi, ty;
+double y, w, th, *res;
+{ double raw;
+  INT tg;
+
+  tg = mi[MTG] & 63;
+  if ((tg==TGAUS) | (tg==TROBT) | (tg==TCAUC))
+    raw = y-res[ZMEAN];
+  else
+    raw = y-w*res[ZMEAN];
+  switch(ty)
+  { case RDEV:
+      if (res[ZDLL]>0) return(sqrt(-2*res[ZLIK]));
+            else return(-sqrt(-2*res[ZLIK]));
+    case RPEAR:
+      if (res[ZDDLL]<=0)
+      { if (res[ZDLL]==0) return(0);
+        return(NOSLN);
+      }
+      return(res[ZDLL]/sqrt(res[ZDDLL]));
+    case RRAW:  return(raw);
+    case RLDOT: return(res[ZDLL]);
+    case RDEV2: return(-2*res[ZLIK]);
+    case RLDDT: return(res[ZDDLL]);
+    case RFIT:  return(th);
+    case RMEAN: return(res[ZMEAN]);
+    default: ERROR(("resid: unknown residual type %d",ty));
+  }
+  return(0.0);
+}
+
+double studentize(res,inl,var,ty,link)
+double res, inl, var, *link;
+int ty;
+{ double den;
+  inl *= link[ZDDLL];
+  var = var*var*link[ZDDLL];
+  if (inl>1) inl = 1;
+  if (var>inl) var = inl;
+  den = 1-2*inl+var;
+  if (den<0) return(0.0);
+  switch(ty)
+  { case RDEV:
+    case RPEAR:
+    case RRAW:
+    case RLDOT:
+      return(res/sqrt(den));
+    case RDEV2:
+      return(res/den);
+    default: return(res);
+  }
+}
+
+void fitted(lf,des,fit,what,cv,st,ty)
+lfit *lf;
+design *des;
+double *fit;
+INT what, cv, st, ty;
+{ INT i, j, d, n, ev;
+  double xx[MXDIM], th, inl, var, link[LLEN];
+    inl = 0.0;
+    var = 0.0;
+  n = lf->mi[MN];
+  d = lf->mi[MDIM];
+  ev = lf->mi[MEV];
+  cv &= (ev!=ECROS);
+  if ((lf->mi[MEV]==EDATA)|(lf->mi[MEV]==ECROS)) ev = EFITP;
+  for (i=0; i<n; i++)
+  { for (j=0; j<d; j++) xx[j] = datum(lf,j,i);
+    th = dointpoint(lf,des,xx,what,ev,i);
+    if ((what==PT0)|(what==PVARI)) th = th*th;
+    if (what==PCOEF)
+    { th += base(lf,i);
+      stdlinks(link,lf,i,th,lf->dp[DRSC]);
+      if ((cv)|(st))
+      { inl = dointpoint(lf,des,xx,PT0,ev,i);
+        inl = inl*inl;
+        if (cv)
+        { th -= inl*link[ZDLL];
+          stdlinks(link,lf,i,th,lf->dp[DRSC]);
+        }
+        if (st) var = dointpoint(lf,des,xx,PNLX,ev,i);
+      }
+      fit[i] = resid(resp(lf,i),prwt(lf,i),th,lf->mi,ty,link);
+      if (st) fit[i] = studentize(fit[i],inl,var,ty,link);
+    } else fit[i] = th;
+    if (lf_error) return;
+  }
+}
+
+#ifdef CVERSION
+extern lfit lf;
+extern design des;
+
+vari *vfitted(type)
+INT type;
+{ vari *v;
+  INT n;
+  n = lf.mi[MN];
+  v = createvar("vfitted",STHIDDEN,n,VDOUBLE);
+  recondat(1,&n);
+  if (lf_error) return(NULL);
+
+  fitted(&lf,&des,vdptr(v),PCOEF,0,0,type);
+  return(v);
+}
+
+void cfitted(v,ty)
+vari *v;
+INT ty;
+{ double *f;
+  vari *vr;
+  INT i, n, cv, st, wh;
+
+  i = getarg(v,"type",1);
+  if (i>0) ty = restyp(argval(v,i));
+
+  i = getarg(v,"cv",1); cv = (i>0) ? getlogic(v,i) : 0;
+  i = getarg(v,"studentize",1); st = (i>0) ? getlogic(v,i) : 0;
+
+  wh = PCOEF;
+  i = getarg(v,"what",1);
+  if (i>0) wh = ppwhat(argval(v,i));
+
+  recondat(ty==5,&n);
+  if (lf_error) return;
+
+  vr = createvar("fitted",STHIDDEN,n,VDOUBLE);
+  f = vdptr(vr);
+  fitted(&lf,&des,f,wh,cv,st,ty);
+
+  saveresult(vr,argarg(v,0),STREGULAR);
+}
+#endif
diff --git a/src/locfit/frend.c b/src/locfit/frend.c
new file mode 100644
index 0000000..7b5f830
--- /dev/null
+++ b/src/locfit/frend.c
@@ -0,0 +1,409 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include "local.h"
+
+extern INT cvi;
+extern double robscale;
+
+double resp(lf,i)
+lfit *lf;
+INT i;
+{ if (lf->y==NULL) return(0.0);
+    return(lf->y[i]);
+}
+
+double prwt(lf,i)
+lfit *lf;
+INT i;
+{ if (i==cvi) return(0.0);
+    if (lf->w==NULL) return(1.0);
+    return(lf->w[i]);
+}
+
+double base(lf,i)
+lfit *lf;
+INT i;
+{ if (lf->base==NULL) return(0.0);
+    return(lf->base[i]);
+}
+
+double cens(lf,i)
+lfit *lf;
+INT i;
+{ if (lf->c==NULL) return(0.0);
+    return(lf->c[i]);
+}
+
+double vocri(lk,t0,t2,pen)
+double lk, t0, t2, pen;
+{ if (pen==0) return(-2*t0*lk/((t0-t2)*(t0-t2)));
+    return((-2*lk+pen*t2)/t0);
+}
+
+INT procvraw(des,lf,v)
+design *des;
+lfit *lf;
+INT v;
+{ INT lf_status;
+    int i;
+    double h, coef[1+MXDIM];
+    des->xev = evpt(lf,v);
+    
+    lf_status = ainitband(des,lf);
+    
+    if (!lf_error) switch(lf->mi[MACRI])
+    { case AKAT:
+        case ACP:
+        case AMDI:
+            h = aband2(des,lf,des->h);
+            h = aband3(des,lf,h);
+            h = nbhd(lf,des,0,h,1);
+            lf_status = locfit(lf,des,h,0);
+            break;
+        case ANONE:
+        case AOK:
+            break;
+    }
+    
+    lf->h[v] = des->h;
+    for (i=0; i<des->ncoef; i++) coef[i] = des->cf[cfn(des,i)];
+    
+    if (!lf_error)
+    { if (lf->mi[MDC]) dercor(des,lf,coef);
+        subparcomp(des,lf,coef);
+        for (i=0; i<des->ncoef; i++) lf->coef[i*lf->nvm+v] =  coef[i];
+    }
+    
+    lf->deg[v] = lf->mi[MDEG];
+    
+    return(lf_status);
+}
+
+/*
+ * Set default values for the likelihood e.t.c. This
+ * is called in cases where the optimization for the fit
+ * has failed.
+ */
+
+void set_default_like(lf,nvm,v,d)
+lfit *lf;
+INT nvm, v;
+int d;
+{ INT i;
+    lf->lik[v] = lf->lik[nvm+v] = 0;
+    lf->lik[2*nvm+v] = 0; /* should use sum of weights here? */
+    for (i=0; i<=d; i++)
+        lf->t0[i*nvm+v] = lf->nlx[i*nvm+v] = 0.0;
+}
+
+INT procv(des,lf,v)
+design *des;
+lfit *lf;
+INT v;
+{ INT d, p, nvm, i, k;
+    double trc[6], t0[1+MXDIM], vari[1+MXDIM];
+    memset(vari, 0, sizeof(vari));
+    k = procvraw(des,lf,v);
+    if (lf_error) return(k);
+    
+    d = lf->mi[MDIM]; p = lf->mi[MP];
+    nvm = lf->nvm;
+    
+    switch(k)
+    { case LF_OK: break;
+        case LF_NCON:
+            WARN(("procv: locfit did not converge"));
+            break;
+        case LF_OOB:
+            WARN(("procv: parameters out of bounds"));
+            break;
+        case LF_PF:
+            if (lf->mi[MDEB]>1) WARN(("procv: perfect fit"));
+            set_default_like(lf,nvm,v,d);
+            return(k);
+        case LF_NOPT:
+            WARN(("procv: no points with non-zero weight"));
+            set_default_like(lf,nvm,v,d);
+            return(k);
+        case LF_INFA:
+            if (lf->mi[MDEB]>1) WARN(("procv: initial value problem"));
+            set_default_like(lf,nvm,v,d);
+            return(k);
+        case LF_DEMP:
+            WARN(("procv: density estimate, empty integration region"));
+            set_default_like(lf,nvm,v,d);
+            return(k);
+        case LF_XOOR:
+            WARN(("procv: fit point outside xlim region"));
+            set_default_like(lf,nvm,v,d);
+            return(k);
+        case LF_DNOP:
+            if (lf->mi[MDEB]>1)
+                WARN(("density estimation -- insufficient points in smoothing window"));
+            set_default_like(lf,nvm,v,d);
+            return(k);
+        case LF_FPROB:
+            WARN(("procv: f problem; likelihood failure"));
+            set_default_like(lf,nvm,v,d);
+            return(k);
+        default:
+            WARN(("procv: unknown return code %d",k));
+            set_default_like(lf,nvm,v,d);
+            return(k);
+    }
+    
+    comp_vari(lf,des,trc,t0);
+    lf->lik[v] = des->llk;
+    lf->lik[nvm+v] = trc[2];
+    lf->lik[2*nvm+v] = trc[0]-trc[2];
+    lf->nlx[v] = sqrt(des->V[0]);
+    
+    for (i=0; i<des->ncoef; i++)
+        vari[i] = des->V[p*cfn(des,0) + cfn(des,i)];
+    vari[0] = sqrt(vari[0]);
+    if (vari[0]>0) for (i=1; i<des->ncoef; i++) vari[i] /= vari[0];
+    t0[0] = sqrt(t0[0]);
+    if (t0[0]>0) for (i=1; i<des->ncoef; i++) t0[i] /= t0[0];
+    
+    subparcomp2(des,lf,vari,t0);
+    for (i=0; i<des->ncoef; i++)
+    { lf->nlx[i*lf->nvm+v] = vari[i];
+        lf->t0[i*lf->nvm+v]  = t0[i];
+    }
+    
+    return(k);
+}
+
+double intvo(des,lf,c0,c1,a,p,t0,t20,t21)
+design *des;
+lfit *lf;
+double *c0, *c1, a, t0, t20, t21;
+INT p;
+{ double th, lk, link[LLEN];
+    INT i;
+    lk = 0;
+    for (i=0; i<des->n; i++)
+    { th = (1-a)*innerprod(c0,&des->X[i*p],p) + a*innerprod(c1,&des->X[i*p],p);
+        stdlinks(link,lf,des->ind[i],th,robscale);
+        lk += des->w[i]*link[ZLIK];
+    }
+    des->llk = lk;
+    return(vocri(des->llk,t0,(1-a)*t20+a*t21,lf->dp[DADP]));
+}
+
+INT procvvord(des,lf,v)
+design *des;
+lfit *lf;
+INT v;
+{ 
+    static const int x_1 = 4;
+    static const int y_1 = 10;
+    double tr[6], gcv, g0, ap, coef[x_1][y_1], t2[4], th, md = 0.0;
+    INT i, j, k = 0, d1, *mi, i0, p1, ip;
+    mi = lf->mi;
+    des->xev = evpt(lf,v);
+    
+    for (i = 0; i < x_1; ++i)
+    {
+        for (j = 0; j < y_1; ++j)
+        {
+            coef[i][j] = 0;
+        }
+    }
+    
+    lf->h[v] = nbhd(lf,des,(INT)(mi[MN]*lf->dp[DALP]),lf->dp[DFXH],0);
+    if (lf->h[v]<=0) WARN(("zero bandwidth in procvvord"));
+    
+    ap = lf->dp[DADP];
+    if ((ap==0) & ((mi[MTG]&63)!=TGAUS)) ap = 2.0;
+    d1 = mi[MDEG]; p1 = mi[MP];
+    for (i=0; i<p1; i++) coef[0][i] = coef[1][i] = coef[2][i] = coef[3][i] = 0.0;
+    i0 = 0; g0 = 0;
+    ip = 1;
+    for (i=mi[MDEG0]; i<=d1; i++)
+    { mi[MDEG] = i; des->p = mi[MP] = calcp(mi,i);
+        k = locfit(lf,des,lf->h[v],0);
+        
+        local_df(lf,des,tr);
+        gcv = vocri(des->llk,tr[0],tr[2],ap);
+        if ((i==mi[MDEG0]) || (gcv<g0)) { i0 = i; g0 = gcv; md = i; }
+        
+        for (j=0; j<des->p; j++) coef[i][j] = des->cf[j];
+        t2[i] = tr[2];
+        
+#ifdef RESEARCH
+        printf("variable order\n");
+        if ((ip) && (i>mi[MDEG0]))
+        { for (j=1; j<10; j++)
+        { gcv = intvo(des,lf,coef[i-1],coef[i],j/10.0,des->p,tr[0],t2[i-1],t2[i]);
+            if (gcv<g0) { g0 = gcv; md = i-1+j/10.0; }
+        }
+        }
+#endif
+    }
+    
+    if (i0<d1) /* recompute the best fit */
+    { mi[MDEG] = i0; des->p = mi[MP] = calcp(mi,i0);
+        k = locfit(lf,des,lf->h[v],0);
+        for (i=mi[MP]; i<p1; i++) des->cf[i] = 0.0;
+        i0 = (INT)md; if (i0==d1) i0--;
+        th = md-i0;
+        for (i=0; i<p1; i++) des->cf[i] = (1-th)*coef[i0][i]+th*coef[i0+1][i];
+        mi[MDEG] = d1; mi[MP] = p1;
+    }
+    
+    for (i=0; i<p1; i++) lf->coef[i*lf->nvm+v] = des->cf[i];
+    lf->deg[v] = md;
+    return(k);
+}
+
+/* special version of ressumm to estimate sigma^2, with derivative estimation */
+void ressummd(lf,des)
+lfit *lf;
+design *des;
+{ INT i;
+    double s0, s1;
+    s0 = s1 = 0.0;
+    if ((lf->mi[MTG]&64)==0)
+    { lf->dp[DRV] = 1.0;
+        return;
+    }
+    for (i=0; i<lf->nv; i++)
+    { s0 += lf->lik[2*lf->nvm+i];
+        s1 += lf->lik[i];
+    }
+    if (s0==0.0)
+        lf->dp[DRV] = 0.0;
+    else
+        lf->dp[DRV] = -2*s1/s0;
+}
+
+void ressumm(lf,des)
+lfit *lf;
+design *des;
+{ INT i, j, ev, tg, orth;
+    double *dp, *oy, pw, r1, r2, rdf, t0, t1, u[MXDIM], link[LLEN];
+    dp = lf->dp;
+    dp[DLK] = dp[DT0] = dp[DT1] = 0;
+    if ((lf->mi[MEV]==EKDCE) | (lf->mi[MEV]==EPRES))
+    { dp[DRV] = 1.0;
+        return;
+    }
+    if (lf->nd>0)
+    { ressummd(lf,des);
+        return;
+    }
+    r1 = r2 = 0.0;
+    ev = lf->mi[MEV];
+    if ((ev==EDATA) | (ev==ECROS)) ev = EFITP;
+    orth = (lf->mi[MGETH]==4) | (lf->mi[MGETH]==5);
+    for (i=0; i<lf->mi[MN]; i++)
+    { for (j=0; j<lf->mi[MDIM]; j++) u[j] = datum(lf,j,i);
+        des->th[i] = base(lf,i)+dointpoint(lf,des,u,PCOEF,ev,i);
+        des->wd[i] = resp(lf,i) - des->th[i];
+        des->w[i] = 1.0;
+        des->ind[i] = i;
+    }
+    
+    tg = lf->mi[MTG];
+    lf->dp[DRSC] = 1.0;
+    if ((tg==TROBT+64) | (tg==TCAUC+64)) /* global robust scale */
+    { oy = lf->y; lf->y = des->wd;
+        des->xev = lf->pc.xbar;
+        locfit(lf,des,0.0,1);
+        lf->y = oy;
+        lf->dp[DRSC] = robscale;
+    }
+    
+    if (orth) /* orthog. residuals */
+    { int od, op;
+        des->n = lf->mi[MN];
+        od = lf->mi[MDEG]; op = lf->mi[MP];
+        lf->mi[MDEG] = 1;
+        lf->mi[MP] = des->p = 1+lf->mi[MDIM];
+        oy = lf->y; lf->y = des->wd;
+        des->xev = lf->pc.xbar;
+        locfit(lf,des,0.0,1);
+        for (i=0; i<lf->mi[MN]; i++) oy[i] = resp(lf,i) - des->th[i];
+        lf->y = oy;
+        lf->mi[MDEG] = od; lf->mi[MP] = op;
+    }
+    
+    for (i=0; i<lf->mi[MN]; i++)
+    { for (j=0; j<lf->mi[MDIM]; j++) u[j] = datum(lf,j,i);
+        t0 = dointpoint(lf,des,u,PT0,ev,i);
+        t1 = dointpoint(lf,des,u,PNLX,ev,i);
+        stdlinks(link,lf,i,des->th[i],lf->dp[DRSC]);
+        t1 = t1*t1*link[ZDDLL];
+        t0 = t0*t0*link[ZDDLL];
+        if (t1>1) t1 = 1;
+        if (t0>1) t0 = 1; /* no observation gives >1 deg.free */
+        dp[DLK] += link[ZLIK];
+        dp[DT0] += t0;
+        dp[DT1] += t1;
+        pw = prwt(lf,i);
+        if (pw>0)
+        { r1 += link[ZDLL]*link[ZDLL]/pw;
+            r2 += link[ZDDLL]/pw;
+        }
+        if (orth) des->di[i]  = t1;
+    }
+    
+    if (orth) return;
+    
+    dp[DRV] = 1.0;
+    if ((lf->mi[MTG]&64)==64) /* quasi family */
+    { rdf = lf->mi[MN]-2*dp[DT0]+dp[DT1];
+        if (rdf<1.0)
+        { WARN(("Estimated rdf < 1.0; not estimating variance"));
+        }
+        else
+            dp[DRV] = r1/r2 * lf->mi[MN] / rdf;
+    }
+    
+    /* try to ensure consistency for family="circ"! */
+    if (((lf->mi[MTG]&63)==TCIRC) & (lf->mi[MDIM]==1))
+    { INT *ind, nv;
+        double dlt, th0, th1;
+        ind = des->ind;
+        nv = lf->nv;
+        for (i=0; i<nv; i++) ind[i] = i;
+        lforder(ind,vdptr(lf->xxev),0,nv-1);
+        for (i=1; i<nv; i++)
+        { dlt = evptx(lf,ind[i],0)-evptx(lf,ind[i-1],0);
+            th0 = lf->coef[ind[i]]-dlt*lf->coef[ind[i]+nv]-lf->coef[ind[i-1]];
+            th1 = lf->coef[ind[i]]-dlt*lf->coef[ind[i-1]+nv]-lf->coef[ind[i-1]];
+            if ((th0>PI)&(th1>PI))
+            { for (j=0; j<i; j++)
+                lf->coef[ind[j]] += 2*PI;
+                i--;
+            }
+            if ((th0<(-PI))&(th1<(-PI)))
+            { for (j=0; j<i; j++)
+                lf->coef[ind[j]] -= 2*PI;
+                i--;
+            }
+        }
+    }
+}
+
+double rss(lf,des,df)
+lfit *lf;
+design *des;
+double *df;
+{ double ss;
+    INT i;
+    ss = 0;
+    if (ident==1)
+    { for (i=0; i<lf->mi[MN]; i++)
+        ss += SQR(resp(lf,i)-lf->coef[i]);
+        *df = lf->mi[MN]-lf->mi[MP];
+        return(ss);
+    }
+    ressumm(lf,des);
+    *df = lf->mi[MN] - 2*lf->dp[DT0] + lf->dp[DT1];
+    return(-2*lf->dp[DLK]);
+}
diff --git a/src/locfit/help.c b/src/locfit/help.c
new file mode 100644
index 0000000..c5bb242
--- /dev/null
+++ b/src/locfit/help.c
@@ -0,0 +1,88 @@
+/*
+ *   Copyright (c) 1996-2000 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *
+ *   The example() function is used to tnterpreting examples
+ *   in the locfit.ceg file.
+ */
+
+#include "local.h"
+
+#ifdef CVERSION
+
+static FILE *help;
+extern char *lfhome;
+
+void example(v)
+vari *v;
+{ int i, j, run, ti;
+  char *z, elin[10], helpfile[100], line[200];
+
+  run = 0;
+  i = getarg(v,"ex",1);
+  ti = (i==0);
+  if (!ti)
+  { z = strchr(argval(v,i),'.');
+    if (z==NULL)
+    { ERROR(("example: invalid number %s",argval(v,i)));
+      return;
+    }
+    j = getarg(v,"run",1);
+    if (j>0) run = getlogic(v,j);
+  }
+
+  if (lfhome!=NULL)
+    sprintf(helpfile,"%s/locfit.ceg",lfhome);
+  else
+    sprintf(helpfile,"locfit.ceg");
+
+  help = fopen(helpfile,"r");
+  if (help==NULL)
+  { ERROR(("Can't find locfit.ceg file -- is LFHOME set?"));
+    return;
+  }
+
+  do
+  { z = fgets(line,190,help);
+    if (z==NULL) /* reached end of file */
+    { if (!ti) ERROR(("No example %s in help file",argval(v,i)));
+      fclose(help);
+      return;
+    }
+    if (line[0]=='e')
+    { sscanf(&line[2],"%s",elin);
+      if (ti) printf("Example %s. ",elin);
+    }
+    else elin[0] = 0;
+    if ((ti) && (line[0]=='t'))
+      printf("%s",&line[2]);
+  }  while ((ti) || (strcmp(elin,argval(v,i))!=0));
+
+  while(1)
+  { z = fgets(line,190,help);
+    switch(z[0])
+    { case 'f': /* end example */
+        fclose(help);
+        printf("\n");
+        return;
+      case 't': /* title */
+        printf("\nExample %s. %s\n",argval(v,i),&line[2]);
+        break;
+      case 'c': /* code */
+        printf("  %s",&line[2]);
+      case 'h': /* hidden code, usually sleep */
+        if (run) makecmd(&line[2]);
+        break;
+      case 'd': /* discussion */
+        printf("%s",&line[2]);
+        break;
+      case 'n': /* no code */
+        printf("There is no code for this example.\n");
+        break;
+    }
+  }
+}
+
+#endif
diff --git a/src/locfit/imatlb.h b/src/locfit/imatlb.h
new file mode 100644
index 0000000..73331f3
--- /dev/null
+++ b/src/locfit/imatlb.h
@@ -0,0 +1,36 @@
+typedef struct {
+  int n;
+  double *dpr;
+} vari;    
+
+typedef struct {
+  double *Z, *Q, *dg, *f2;
+  int p, sm; } xtwxstruc;
+
+typedef struct {
+  vari *wk;
+  double *coef, *xbar, *f;
+  xtwxstruc xtwx; } paramcomp;
+
+typedef struct {
+  vari *dw, *index;
+  double *xev, *X, *w, *di, *res, *th, *wd, h, xb[15];
+  double *V, *P, *f1, *ss, *oc, *cf, llk;
+  xtwxstruc xtwx;
+  int *ind, n, p, pref, (*itype)();
+  int (*vfun)(); } design;
+
+typedef struct {
+  vari *tw, *L, *iw, *xxev;
+  double *x[15], *y, *w, *base, *c, *xl;
+  double *coef, *nlx, *t0, *lik, *h, *deg;
+  double *sv, *fl, *sca, *dp, kap[3];
+  int *ce, *s, *lo, *hi, sty[15];
+  int *mg, nvm, ncm, vc;
+  int nl, nv, nnl, nce, nk, nn, *mi, ord, deriv[9], nd;
+  paramcomp pc;
+  varname yname, xname[15], wname, bname, cname; } lfit;
+
+extern void mlbcall(
+             double *x, double *y,
+             double *xx, double *ff, int n);
diff --git a/src/locfit/lf_dercor.c b/src/locfit/lf_dercor.c
new file mode 100644
index 0000000..e4a5e65
--- /dev/null
+++ b/src/locfit/lf_dercor.c
@@ -0,0 +1,51 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *   Derivative corrections. The local slopes are not the derivatives
+ *   of the local likelihood estimate; the function dercor() computes
+ *   the adjustment to get the correct derivatives under the assumption
+ *   that h is constant.
+ *
+ *   By differentiating the local likelihood equations, one obtains
+ *
+ *     d ^      ^       T      -1   T  d    .       ^
+ *    -- a   =  a  -  (X W V X)    X  -- W  l( Y, X a)
+ *    dx  0      1                    dx
+ */
+
+#include "local.h"
+extern double robscale;
+
+void dercor(des,lf,coef)
+design *des;
+lfit *lf;
+double *coef;
+{ double s1, dc[MXDIM], wd, link[LLEN];
+  INT i, ii, j, m, p, d, *mi; 
+  mi = lf->mi;
+  if (mi[MTG]<=THAZ) return;
+  if (mi[MKER]==WPARM) return;
+
+  d = mi[MDIM];
+  p = des->p; m = des->n;
+
+  if (mi[MDEB]>1) printf("  Correcting derivatives\n");
+  fitfun(lf,des->xev,des->xev,des->f1,NULL,0);
+  jacob_solve(&des->xtwx,des->f1);
+  setzero(dc,d);
+
+  /* correction term is e1^T (XTWVX)^{-1} XTW' ldot. */
+  for (i=0; i<m; i++)
+  { s1 = innerprod(des->f1,&des->X[i*p],p);
+    ii = des->ind[i];
+    stdlinks(link,lf,ii,des->th[i],robscale);
+    for (j=0; j<d; j++)
+    { wd = des->w[i]*weightd(datum(lf,j,ii)-des->xev[j],lf->sca[j],d,mi[MKER],mi[MKT],des->h,lf->sty[j],des->di[ii]);
+      dc[j] += s1*wd*link[ZDLL];
+    }
+
+  }
+  for (j=0; j<d; j++) coef[j+1] += dc[j];
+}
diff --git a/src/locfit/lf_fitfun.c b/src/locfit/lf_fitfun.c
new file mode 100644
index 0000000..f7b1f32
--- /dev/null
+++ b/src/locfit/lf_fitfun.c
@@ -0,0 +1,258 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *   Evaluate the locfit fitting functions.
+ *     calcp(mi,deg)
+ *       calculates the number of fitting functions.
+ *     makecfn(des,lf)
+ *       makes the coef.number vector.
+ *     fitfun(lf,x,t,f,der,nd)
+ *       lf is the local fit structure.
+ *       x is the data point.
+ *       t is the fitting point.
+ *       f is a vector to return the results.
+ *       deriv is a vector of derivative variables.
+ *       nd is the number of derivatives.
+ *     designmatrix(lf, des)
+ *       is a wrapper for fitfun to build the design matrix.
+ *
+ */
+
+#include "local.h"
+
+INT calcp(mi,deg)
+INT *mi, deg;
+{ INT i, k;
+    
+    if (mi[MUBAS]) return(mi[MP]);
+    
+    switch (mi[MKT])
+    { case KSPH:
+        case KCE:
+            k = 1;
+            for (i=1; i<=deg; i++) k = k*(mi[MDIM]+i)/i;
+            return(k);
+        case KPROD: return(mi[MDIM]*deg+1);
+        case KLM: return(mi[MDIM]);
+    }
+    ERROR(("calcp: invalid kt %d",mi[MKT]));
+    return(0);
+}
+
+INT coefnumber(deriv,nd,kt,d,deg)
+INT *deriv, nd, kt, d, deg;
+{ INT d0, d1, t;
+    
+    if (d==1)
+    { if (nd<=deg) return(nd);
+        return(-1);
+    }
+    
+    if (nd==0) return(0);
+    if (deg==0) return(-1);
+    if (nd==1) return(1+deriv[0]);
+    if (deg==1) return(-1);
+    if (kt==KPROD) return(-1);
+    
+    if (nd==2)
+    { d0 = deriv[0]; d1 = deriv[1];
+        if (d0<d1) { t = d0; d0 = d1; d1 = t; }
+        return((d+1)*(d0+1)-d0*(d0+3)/2+d1);
+    }
+    if (deg==2) return(-1);
+    
+    ERROR(("coefnumber not programmed for nd>=3"));
+    return(-1);
+}
+
+void makecfn(des,lf)
+design *des;
+lfit *lf;
+{ int i;
+    INT *mi, nd;
+    
+    nd = lf->nd;
+    mi = lf->mi;
+    
+    des->cfn[0] = coefnumber(lf->deriv,nd,mi[MKT],mi[MDIM],mi[MDEG]);
+    des->ncoef = 1;
+    if (nd >= mi[MDEG]) return;
+    if (mi[MDIM]>1)
+    { if (nd>=2) return;
+        if ((nd>=1) && (mi[MKT]==KPROD)) return;
+    }
+    
+    for (i=0; i<mi[MDIM]; i++)
+    { lf->deriv[nd] = i;
+        des->cfn[i+1] = coefnumber(lf->deriv,nd+1,mi[MKT],mi[MDIM],mi[MDEG]);
+    }
+    des->ncoef = 1+mi[MDIM];
+}
+
+void fitfunangl(dx,ff,sca,cd,deg)
+double dx, *ff, sca;
+INT deg, cd;
+{
+    if (deg>=3) WARN(("Can't handle angular model with deg>=3"));
+    
+    switch(cd)
+    { case 0:
+            ff[0] = 1;
+            ff[1] = sin(dx/sca)*sca;
+            ff[2] = (1-cos(dx/sca))*sca*sca;
+            return;
+        case 1:
+            ff[0] = 0;
+            ff[1] = cos(dx/sca);
+            ff[2] = sin(dx/sca)*sca;
+            return;
+        case 2:
+            ff[0] = 0;
+            ff[1] = -sin(dx/sca)/sca;
+            ff[2] = cos(dx/sca);
+            return;
+        default: WARN(("Can't handle angular model with >2 derivs"));
+    }
+}
+
+void fitfun(lf,x,t,f,deriv,nd)
+lfit *lf;
+double *x, *t, *f;
+INT *deriv, nd;
+{ 
+    INT d, deg, m, i, j, k, ct_deriv[MXDIM];
+    double ff[MXDIM][1+MXDEG], dx[MXDIM];
+    
+#ifdef SVERSION
+    if (lf->mi[MUBAS])
+    { if (nd>0) WARN(("User basis does not take derivatives"));
+        basis(x,t,f,lf->mi[MDIM],lf->mi[MP]);
+        return;
+    }
+#endif
+    
+    d = lf->mi[MDIM];
+    deg = lf->mi[MDEG];
+    m = 0;
+    
+    if (lf->mi[MKT]==KLM)
+    { for (i=0; i<d; i++) f[m++] = x[i];
+        return;
+    }
+    memset(ct_deriv, 0, sizeof(ct_deriv));
+    for (i = 0; i < MXDIM; ++i)
+    {
+        for (j = 0; j < 1 + MXDEG; ++j)
+        {
+            ff[i][j] = 0;
+        }
+    }
+    
+    f[m++] = (nd==0);
+    if (deg==0) return;
+    
+    for (i=0; i<d; i++)
+    { ct_deriv[i] = 0;
+        dx[i] = (t==NULL) ? x[i] : x[i]-t[i];
+    }
+    for (i=0; i<nd; i++) ct_deriv[deriv[i]]++;
+    
+    for (i=0; i<d; i++)
+    { switch(lf->sty[i])
+        {
+            case STANGL:
+                fitfunangl(dx[i],ff[i],lf->sca[i],ct_deriv[i],lf->mi[MDEG]);
+                break;
+            default:
+                for (j=0; j<ct_deriv[i]; j++) ff[i][j] = 0.0;
+                ff[i][ct_deriv[i]] = 1.0;
+                for (j=ct_deriv[i]+1; j<=deg; j++)
+                    ff[i][j] = ff[i][j-1]*dx[i]/(j-ct_deriv[i]);
+        }
+    }
+    
+    /*
+     *  Product kernels. Note that if ct_deriv[i] != nd, that implies
+     *  there is differentiation wrt another variable, and all components
+     *  involving x[i] are 0.
+     */
+    if ((d==1) || (lf->mi[MKT]==KPROD))
+    { for (j=1; j<=deg; j++)
+        for (i=0; i<d; i++)
+            f[m++] = (ct_deriv[i]==nd) ? ff[i][j] : 0.0;
+        return;
+    }
+    
+    /*
+     *  Spherical kernels with the full polynomial basis.
+     *  Presently implemented up to deg=3.
+     */
+    for (i=0; i<d; i++)
+        f[m++] = (ct_deriv[i]==nd) ? ff[i][1] : 0.0;
+    if (deg==1) return;
+    
+    for (i=0; i<d; i++)
+    {
+        /* xi^2/2 terms. */
+        f[m++] = (ct_deriv[i]==nd) ? ff[i][2] : 0.0;
+        
+        /* xi xj terms */
+        for (j=i+1; j<d; j++)
+            f[m++] = (ct_deriv[i]+ct_deriv[j]==nd) ? ff[i][1]*ff[j][1] : 0.0;
+    }
+    if (deg==2) return;
+    
+    for (i=0; i<d; i++)
+    { 
+        /* xi^3/6 terms */
+        f[m++] = (ct_deriv[i]==nd) ? ff[i][3] : 0.0;
+        
+        /* xi^2/2 xk terms */
+        for (k=i+1; k<d; k++)
+            f[m++] = (ct_deriv[i]+ct_deriv[k]==nd) ? ff[i][2]*ff[k][1] : 0.0;
+        
+        /* xi xj xk terms */
+        for (j=i+1; j<d; j++)
+        { f[m++] = (ct_deriv[i]+ct_deriv[j]==nd) ? ff[i][1]*ff[j][2] : 0.0;
+            for (k=j+1; k<d; k++)
+                f[m++] = (ct_deriv[i]+ct_deriv[j]+ct_deriv[k]==nd) ?
+                ff[i][1]*ff[j][1]*ff[k][1] : 0.0;
+        }
+    }
+    if (deg==3) return;
+    
+    ERROR(("fitfun: can't handle deg=%d for spherical kernels",deg));
+}
+
+/*
+ *  Build the design matrix. Assumes des->ind contains the indices of
+ *  the required data points; des->n the number of points; des->xev
+ *  the fitting point.
+ */
+void designmatrix(lf,des)
+lfit *lf;
+design *des;
+{ int i, ii, j, p;
+    double *X, u[MXDIM];
+    
+    X = d_x(des);
+    p = des->p;
+    
+    if (lf->mi[MUBAS])
+    {
+#ifdef SVERSION
+        vbasis(lf->x,des->xev,lf->mi[MN],lf->mi[MDIM],des->ind,des->n,p,X);
+#else
+        ERROR(("user basis in S version only\n"));
+#endif
+        return;
+    }
+    
+    for (i=0; i<des->n; i++)
+    { ii = des->ind[i];
+        for (j=0; j<lf->mi[MDIM]; j++) u[j] = datum(lf,j,ii);
+        fitfun(lf,u,des->xev,&X[i*p],NULL,(INT)0);
+    }
+}
diff --git a/src/locfit/lf_robust.c b/src/locfit/lf_robust.c
new file mode 100644
index 0000000..589b9d4
--- /dev/null
+++ b/src/locfit/lf_robust.c
@@ -0,0 +1,127 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *   This file includes functions to solve for the scale estimate in
+ *   local robust regression and likelihood. The main entry point is
+ *   lf_robust(lf,des,noit),
+ *   called from the locfit() function.
+ *
+ *   The update_rs(x) accepts a residual scale x as the argument (actually,
+ *   it works on the log-scale). The function computes the local fit
+ *   assuming this residual scale, and re-estimates the scale from this
+ *   new fit. The final solution satisfies the fixed point equation
+ *   update_rs(x)=x. The function lf_robust() automatically calls
+ *   update_rs() through the fixed point iterations.
+ *
+ *   The estimation of the scale from the fit is based on the sqrt of
+ *   the median deviance of observations with non-zero weights (in the
+ *   gaussian case, this is the median absolute residual).
+ *
+ *   TODO:
+ *     Should use smoothing weights in the median.
+ */
+
+#include "local.h"
+
+void lfiter(lfit* lf, design* des);
+
+extern int lf_status;
+double robscale;
+
+static lfit *rob_lf;
+static design *rob_des;
+
+double median(x,n)
+double *x;
+INT n;
+{ INT i, j, lt, eq, gt;
+  double lo, hi, s;
+  lo = hi = x[0];
+  for (i=0; i<n; i++)
+  { lo = MIN(lo,x[i]);
+    hi = MAX(hi,x[i]);
+  }
+  if (lo==hi) return(lo);
+  lo -= (hi-lo);
+  hi += (hi-lo);
+  for (i=0; i<n; i++)
+  { if ((x[i]>lo) & (x[i]<hi))
+    { s = x[i]; lt = eq = gt = 0;
+      for (j=0; j<n; j++)
+      { lt += (x[j]<s);
+        eq += (x[j]==s);
+        gt += (x[j]>s);
+      }
+      if ((2*(lt+eq)>n) && (2*(gt+eq)>n)) return(s);
+      if (2*(lt+eq)<=n) lo = s;
+      if (2*(gt+eq)<=n) hi = s;
+    }
+  }
+  return((hi+lo)/2);
+}
+
+double nrobustscale(lf,des,rs)
+lfit *lf;
+design *des;
+double rs;
+{ int i, ii, p;
+  double link[LLEN], sc, sd, sw, e;
+  p = des->p; sc = sd = sw = 0.0;
+  for (i=0; i<des->n; i++)
+  { ii = des->ind[i];
+    des->th[i] = base(lf,ii)+innerprod(des->cf,d_xi(des,i),p);
+    e = resp(lf,ii)-des->th[i];
+    stdlinks(link,lf,ii,des->th[i],rs);
+    sc += des->w[i]*e*link[ZDLL];
+    sd += des->w[i]*e*e*link[ZDDLL];
+    sw += des->w[i];
+  }
+
+  /* newton-raphson iteration for log(s)
+     -psi(ei/s) - log(s); s = e^{-th}
+  */
+  rs *= exp((sc-sw)/(sd+sc));
+  return(rs);
+}
+
+double robustscale(lf,des)
+lfit *lf;
+design *des;
+{ INT i, ii, p;
+  double rs, link[LLEN];
+  p = des->p;
+  for (i=0; i<des->n; i++)
+  { ii = des->ind[i];
+    des->th[i] = base(lf,ii) + innerprod(des->cf,d_xi(des,i),p);
+    links(des->th[i],resp(lf,ii),lf->mi[MTG]&127,lf->mi[MLINK],link,cens(lf,ii),prwt(lf,ii),1.0);
+    des->res[i] = -2*link[ZLIK];
+  }
+  rs = sqrt(median(des->res,des->n));
+  if (rs==0.0) rs = 1.0;
+  return(rs);
+}
+
+double update_rs(x)
+double x;
+{
+  if (lf_status != LF_OK) return(x);
+  robscale = exp(x);
+  lfiter(rob_lf,rob_des);
+  if (lf_status != LF_OK) return(x);
+
+  return(log(robustscale(rob_lf,rob_des)));
+}
+
+void lf_robust(lf,des)
+lfit *lf;
+design *des;
+{ double x;
+  rob_lf = lf;
+  rob_des = des;
+  lf_status = LF_OK;
+
+  x = log(robustscale(lf,des));
+  solve_fp(update_rs, x, 1.0e-6, (int)lf->mi[MMXIT]);
+}
diff --git a/src/locfit/lf_vari.c b/src/locfit/lf_vari.c
new file mode 100644
index 0000000..984063e
--- /dev/null
+++ b/src/locfit/lf_vari.c
@@ -0,0 +1,157 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *   Post-fitting functions to compute the local variance and
+ *   influence functions. Also the local degrees of freedom
+ *   calculations for adaptive smoothing.
+ */
+
+#include "local.h"
+
+extern double robscale;
+
+/*
+  vmat() computes (after the local fit..) the matrix 
+  M2  = X^T W^2 V X.
+  M12 = (X^T W V X)^{-1} M2
+  Also, for convenience, tr[0] = sum(wi) tr[1] = sum(wi^2).
+*/
+void vmat(lf, des, M12, M2, tr)
+lfit *lf;
+design *des;
+double *M12, *M2, *tr;
+{ INT i, p, nk, ok;
+  double link[LLEN], h, ww;
+  p = des->p;
+  setzero(M2,p*p);
+
+  nk = -1;
+
+  /* for density estimation, use integral rather than
+     sum form, if W^2 is programmed...
+  */
+  if ((lf->mi[MTG]<=THAZ) && (lf->mi[MLINK]==LLOG))
+  { switch(lf->mi[MKER])
+    { case WGAUS: nk = WGAUS; h = des->h/SQRT2; break;
+      case WRECT: nk = WRECT; h = des->h; break;
+      case WEPAN: nk = WBISQ; h = des->h; break;
+      case WBISQ: nk = WQUQU; h = des->h; break;
+      case WTCUB: nk = W6CUB; h = des->h; break;
+      case WEXPL: nk = WEXPL; h = des->h/2; break;
+    }
+  }
+
+  tr[0] = tr[1] = 0.0;
+  if (nk != -1)
+  { ok = lf->mi[MKER]; lf->mi[MKER] = nk;
+/* compute M2 using integration. Use M12 as work matrix. */
+    (des->itype)(des->xev, M2, M12, lf, des->cf, h);
+    lf->mi[MKER] = ok;
+    if (lf->mi[MTG]==TDEN) multmatscal(M2,lf->dp[DSWT],p*p);
+    tr[0] = des->ss[0];
+    tr[1] = M2[0]; /* n int W e^<a,A> */
+  }
+  else
+  { for (i=0; i<des->n; i++)
+    { stdlinks(link,lf,des->ind[i],des->th[i],robscale);
+      ww = SQR(des->w[i])*link[ZDDLL];
+      tr[0] += des->w[i];
+      tr[1] += SQR(des->w[i]);
+      addouter(M2,d_xi(des,i),d_xi(des,i),p,ww);
+    }
+  }
+
+  memcpy(M12,M2,p*p*sizeof(double));
+  for (i=0; i<p; i++)
+    jacob_solve(&des->xtwx,&M12[i*p]);
+}
+
+/* Compute influence function and estimated derivatives.
+ * Results stored in des->f1.
+ * This assumes weight function is scaled so that W(0)=1.0.
+ */
+double comp_infl(lf,des)
+lfit *lf;
+design *des;
+{ unitvec(des->f1,0,des->p);
+  jacob_solve(&des->xtwx,des->f1);
+  return(des->f1[0]);
+}
+
+void comp_vari(lf,des,tr,t0)
+lfit *lf;
+design *des;
+double *tr, *t0;
+{ int i, j, k, p;
+  double *M12, *M2;
+  M12 = des->V; M2 = des->P; p = des->p;
+  vmat(lf,des,M12,M2,tr); /* M2 = X^T W^2 V X  tr0=sum(W) tr1=sum(W*W) */
+  tr[2] = m_trace(M12,p);   /* tr (XTWVX)^{-1}(XTW^2VX) */
+
+  comp_infl(lf,des);
+  for (i=0; i<=lf->mi[MDIM]; i++) t0[i] = des->f1[i];
+
+/*
+ * Covariance matrix is M1^{-1} * M2 * M1^{-1}
+ * We compute this using the cholesky decomposition of
+ * M2; premultiplying by M1^{-1} and squaring. This
+ * is more stable than direct computation in near-singular cases.
+ */
+  chol_dec(M2,p);
+  for (i=0; i<p; i++) jacob_solve(&des->xtwx,&M2[i*p]);
+  for (i=0; i<p; i++)
+  { for (j=0; j<p; j++)
+    { M12[i*p+j] = 0;
+      for (k=0; k<p; k++)
+        M12[i*p+j] += M2[k*p+i]*M2[k*p+j]; /* ith column of covariance */
+    }
+  }
+  if ((lf->mi[MTG]==TDEN) && (lf->mi[MLINK]==LIDENT))
+    multmatscal(M12,1/SQR(lf->dp[DSWT]),p*p);
+}
+
+/* local_df computes:
+ *   tr[0] = trace(W)
+ *   tr[1] = trace(W*W)
+ *   tr[2] = trace( M1^{-1} M2 )
+ *   tr[3] = trace( M1^{-1} M3 )
+ *   tr[4] = trace( (M1^{-1} M2)^2 )
+ *   tr[5] = var(theta-hat).
+ */
+void local_df(lf,des,tr)
+lfit *lf;
+design *des;
+double *tr;
+{ int i, j, p;
+  double *m2, *V, ww, link[LLEN];
+
+  tr[0] = tr[1] = tr[2] = tr[3] = tr[4] = tr[5] = 0.0;
+  m2 = des->V; V = des->P; p = des->p;
+
+  vmat(lf,des,m2,V,tr);  /* M = X^T W^2 V X  tr0=sum(W) tr1=sum(W*W) */
+  tr[2] = m_trace(m2,p);   /* tr (XTWVX)^{-1}(XTW^2VX) */
+
+  unitvec(des->f1,0,p);
+  jacob_solve(&des->xtwx,des->f1);
+  for (i=0; i<p; i++)
+    for (j=0; j<p; j++)
+    { tr[4] += m2[i*p+j]*m2[j*p+i];  /* tr(M^2) */
+      tr[5] += des->f1[i]*V[i*p+j]*des->f1[j]; /* var(thetahat) */
+  }
+  tr[5] = sqrt(tr[5]);
+
+  setzero(m2,p*p);
+  for (i=0; i<des->n; i++)
+  { stdlinks(link,lf,des->ind[i],des->th[i],robscale);
+    ww = SQR(des->w[i])*des->w[i]*link[ZDDLL];
+    addouter(m2,d_xi(des,i),d_xi(des,i),p,ww);
+  }
+  for (i=0; i<p; i++)
+  { jacob_solve(&des->xtwx,&m2[i*p]);
+    tr[3] += m2[i*(p+1)];
+  }
+
+  return;
+}
diff --git a/src/locfit/lfcons.h b/src/locfit/lfcons.h
new file mode 100644
index 0000000..0a7ae93
--- /dev/null
+++ b/src/locfit/lfcons.h
@@ -0,0 +1,280 @@
+/*
+ *   Copyright (c) 1998 Lucent Technologies.
+ *   See README file for details.
+ */
+
+/*
+  Numeric values for constants used in locfit
+*/
+
+/*
+  MXDIM and MXDEG are maximum dimension and local polynomial
+  degree for Locfit. Note that some parts of the code may be
+  more restrictive.
+*/
+#define MXDIM 15
+#define MXDEG 7
+
+/*
+  floating point constants
+*/
+#ifndef PI
+#define PI    3.141592653589793238462643
+#endif
+#define S2PI  2.506628274631000502415765
+#define SQRT2 1.4142135623730950488
+#define SQRPI 1.77245385090552
+#define LOGPI 1.144729885849400174143427
+#define GOLDEN 0.61803398874989484820
+#define HL2PI 0.91893853320467267 /* log(2pi)/2 */
+#define SQRPI 1.77245385090552    /* sqrt(pi)   */
+
+/*
+  Criteria for adaptive local fitting  mi[MACRI]
+  1: localized CP;  2: ICI (katkovnik);  3: curvature model index
+  4: Increase bandwidth until locfit returns LF_OK
+*/
+#define ANONE 0
+#define ACP  1
+#define AKAT 2
+#define AMDI 3
+#define AOK  4
+
+/*
+  vector of double precision parameters.
+  0, 1, 2 are the three components of the smoothing parameter.
+  3 cut parameter for adaptive evaluation structures.
+  4-8 are likelihood, degrees of freedom and residual variance,
+  computed as part of the fit.
+  Stored as the lf.dp vector.
+*/
+#define DALP 0
+#define DFXH 1
+#define DADP 2
+#define DCUT 3
+#define DLK  4
+#define DT0  5
+#define DT1  6
+#define DRV  7
+#define DSWT 8
+#define DRSC 9
+#define LEND 10
+
+/*
+  Evaluation structures mi[MEV]
+  EFITP special for `interpolation' at fit points
+*/
+#define ENULL  0
+#define ETREE  1
+#define EPHULL 2
+#define EDATA  3
+#define EGRID  4
+#define EKDTR  5
+#define EKDCE  6
+#define ECROS  7
+#define EPRES  8
+#define EXBAR  9
+#define ENONE  10
+#define EFITP  50
+
+/*
+  integer parameters: sample size; dimension; number of local parameters etc.
+  stored as the lf.mi vector.
+*/
+#define MN     0
+#define MP     1
+#define MDEG0  2
+#define MDEG   3
+#define MDIM   4
+#define MACRI  5
+#define MKER   6
+#define MKT    7
+#define MIT    8
+#define MMINT  9
+#define MMXIT 10
+#define MREN  11
+#define MEV   12
+#define MTG   13
+#define MLINK 14
+#define MDC   15
+#define MK    16
+#define MDEB  17
+#define MGETH 18
+#define MPC   19
+#define MUBAS 20
+#define LENM  21
+
+/*
+  Link functions mi[MLINK].
+  Mostly as in table 4.1 of the book.
+  LDEFAU and LCANON are used to select default and canonical
+  links respectively. LINIT shouldn't be selected by user...
+*/
+#define LINIT  0
+#define LDEFAU 1
+#define LCANON 2
+#define LIDENT 3
+#define LLOG   4
+#define LLOGIT 5
+#define LINVER 6
+#define LSQRT  7
+#define LASIN  8
+
+/*
+  components of vector returned by the links() function
+  in family.c. ZLIK the likelihood; ZMEAN = estimated mean;
+  ZDLL = derivative of log-likelihood; ZDDLL = - second derivative
+*/
+#define LLEN  4
+#define ZLIK  0
+#define ZMEAN 1
+#define ZDLL  2
+#define ZDDLL 3
+
+/*
+  weight functions mi[MKER].
+  see Table 3.1 or the function W() in weights.c for definitions.
+*/
+#define WRECT 1
+#define WEPAN 2
+#define WBISQ 3
+#define WTCUB 4
+#define WTRWT 5
+#define WGAUS 6
+#define WTRIA 7
+#define WQUQU 8
+#define W6CUB 9
+#define WMINM 10
+#define WEXPL 11
+#define WMACL 12
+#define WPARM 13
+
+/*
+  type of multivariate weight function mi[MKT]
+  KSPH (spherical)  KPROD (product)
+  others shouldn't be used at present.
+*/
+#define KSPH   1
+#define KPROD  2
+#define KCE    3
+#define KLM    4
+
+#define STANGL 4
+#define STLEFT 5
+#define STRIGH 6
+#define STCPAR 7
+
+/*
+  Local likelihood family mi[MTG]
+  for quasi-likelihood, add 64.
+*/
+#define TNUL 0
+#define TDEN 1
+#define TRAT 2
+#define THAZ 3
+#define TGAUS 4
+#define TLOGT 5
+#define TPOIS 6
+#define TGAMM 7
+#define TGEOM 8
+#define TCIRC 9
+#define TROBT 10
+#define TRBIN 11
+#define TWEIB 12
+#define TCAUC 13
+#define TPROB 14
+
+/*
+  Integration type mi[MIT] for integration in
+  density estimation.
+*/
+#define INVLD 0
+#define IDEFA 1
+#define IMULT 2
+#define IPROD 3
+#define IMLIN 4
+#define IHAZD 5
+#define IMONT 7
+
+/*
+  For prediction functions, what to predict?
+  PCOEF -- coefficients        PT0   -- influence function
+  PNLX  -- ||l(x)||            PBAND -- bandwidth h(x)
+  PDEGR -- local poly. degree  PLIK  -- max. local likelihood
+  PRDF  -- local res. d.f.     PVARI -- ||l(x)||^2
+*/
+#define PCOEF 1
+#define PT0   2
+#define PNLX  3
+#define PBAND 4
+#define PDEGR 5
+#define PLIK  6
+#define PRDF  7
+#define PVARI 8
+
+/*
+  Residual Types
+*/
+#define RDEV  1
+#define RPEAR 2
+#define RRAW  3
+#define RLDOT 4
+#define RDEV2 5
+#define RLDDT 6
+#define RFIT  7
+#define RMEAN 8
+
+/*
+  components of the colour vector
+*/
+#define CBAK 0
+#define CAXI 1
+#define CTEX 2
+#define CLIN 3
+#define CPOI 4
+#define CCON 5
+#define CCLA 6
+#define CSEG 7
+#define CPA1 8
+#define CPA2 9
+
+/*
+  variable types: double, INT, char, argument list
+*/
+#define VDOUBLE 0
+#define VINT    1
+#define VCHAR   2
+#define VARGL   3
+#define VPREP   4
+#define VARC    5
+#define VVARI   6
+#define VXYZ    7
+
+/*
+  variable status
+*/
+#define STEMPTY   0
+#define STREGULAR 1
+#define STHIDDEN  3
+#define STPLOTVAR 4
+#define STSYSTEM  5
+#define STSYSPEC  6
+#define STREADFI  7
+
+/*
+  return status for the locfit() function
+*/
+#define LF_OK   0
+#define LF_OOB  2   /* out of bounds, or large unstable parameter */
+#define LF_PF   3   /* perfect fit; interpolation; deviance=0 */
+#define LF_NCON 4   /* not converged */
+#define LF_NOPT 6   /* no or insufficient points with non-zero wt */
+#define LF_INFA 7   /* initial failure e.g. log(0) */
+#define LF_DEMP 10  /* density -- empty integration region */
+#define LF_XOOR 11  /* density -- fit point outside xlim region */
+#define LF_DNOP 12  /* density version of 6 */
+#define LF_FPROB 80
+#define LF_BADP 81  /* bad parameters e.g. neg prob for binomial */
+#define LF_LNK  82  /* invalid link */
+#define LF_FAM  83  /* invalid family */
+#define LF_ERR  99  /* error */
diff --git a/src/locfit/lfd.c b/src/locfit/lfd.c
new file mode 100644
index 0000000..4a6b74b
--- /dev/null
+++ b/src/locfit/lfd.c
@@ -0,0 +1,319 @@
+/*
+ *   Copyright (c) 1996-2000 Lucent Technologies.
+ *   See README file for details.
+ */
+
+
+/* Functions for reading/writing to LFData directory */
+
+#include <unistd.h>
+#include "local.h"
+
+#ifdef CVERSION
+
+FILE *lfd=NULL;
+extern char *lfhome;
+char filename[100];
+
+void closefile()
+{ fclose(lfd);
+  lfd = NULL;
+}
+
+void openfile(mode)
+char *mode;
+{ if (lfd!=NULL) closefile();
+  lfd = fopen(filename,mode);
+}
+
+/*
+ *  setfilename() places name[] in the filename[] array.
+ *    -- quotes are stripped from name.
+ *    -- ext is the default extension; .ext is added as an extension.
+ *       (unless fp=1).
+ *       If ext = "lfd" or "fit", the LFData (and paths) are used.
+ *    -- mode is a standard unix mode ("r", "w" etc)
+ *    -- fp indicates the full path is given (used by Windoze GUI).
+ *    -- checks for validity of filename and mode.
+ *    -- returns 0 for successful, 1 for unsuccessful.
+ */
+INT setfilename(name,ext,mode,fp)
+char *name, *ext, *mode;
+INT fp;
+{ char subp[20];
+  int n, quote, use_lfd;
+
+  n = strlen(name);
+  quote = ((name[0]=='"') && (name[n-1]=='"'));
+  if (quote)
+  { name++;
+    name[n-2] = '\0';
+  }
+
+  use_lfd = (strcmp(ext,"lfd")==0) | (strcmp(ext,"fit")==0);
+  if (fp)
+    sprintf(filename,"%s",name);
+  else
+  { if (use_lfd)
+      sprintf(subp,"LFData%c",DIRSEP);
+    else
+      sprintf(subp,"");
+    if (strlen(ext)==0)
+      sprintf(filename,"%s%s",subp,name);
+    else
+      sprintf(filename,"%s%s.%s",subp,name,ext);
+  }
+  if (quote) name[n-2] = '"';
+
+/*
+ * If we are writing, check the file is writeable and that the
+ * LFData directory exists.
+ */
+  if ((mode[0]=='w') | (mode[0]=='a'))
+  { if (use_lfd)
+    { if (access("LFData",F_OK)==-1)
+      { if (access(".",W_OK)==0)
+        { printf("Creating LFData Directory...\n");
+          system("mkdir LFData");
+        }
+      }
+      if (access("LFData",W_OK)==-1)
+      { ERROR(("LFData directory not writeable"));
+        return(0);
+      }
+    }
+    return(1);  /* definitive test is whether fopen works. */
+  }
+
+/*
+ *  If we are reading, check the file exists.
+ *  If it doesn't and use_lfd is true, also check a defined lfhome.
+ */
+  if (mode[0]=='r')
+  { if (access(filename,R_OK)==0) return(1);
+
+    if ((use_lfd) && (lfhome!=NULL)) /* search system lfhome */
+    { if (quote) name[n-2] = '\0';
+      sprintf(filename,"%s/%s%s.%s",lfhome,subp,name,ext);
+      if (quote) name[n-2] = '"';
+      return(access(filename,R_OK)==0);
+    }
+
+    return(0);
+  }
+  ERROR(("setfilename: invalid mode %s",mode));
+  return(0);
+}
+
+void readchar(c,n)
+char *c;
+INT n;
+{ fread(c,1,n,lfd);
+}
+
+void readstr(z)
+char *z;
+{ while(1)
+  { readchar(z,1);
+    if (*z=='\0') return;
+    z++;
+  }
+}
+
+void dumpchar(c,n)
+char *c;
+INT n;
+{ fwrite(c,1,n,lfd);
+}
+
+void dumpstr(z)
+char *z;
+{ dumpchar(z,strlen(z)+1);
+}
+
+#define LFDATAID -281
+
+void dosavedata(v,fp)
+vari *v;
+int fp;
+{ void (*fn)(), (*fs)();
+  INT i, n;
+  char *name;
+  vari *v1;
+  if (argarg(v,0)==NULL)
+  { ERROR(("savedata: no filename"));
+    return;
+  }
+  name = argarg(v,0);
+
+  if (setfilename(name,"lfd","wb",fp)==0)
+  { ERROR(("savedata: cannot access file %s",filename));
+    return;
+  }
+  openfile("wb");
+  if (lf_error) return;
+  fn = dumpchar;
+  fs = dumpstr;
+
+  i = LFDATAID;
+  (*fn)(&i, sizeof(INT));
+  n = 0;
+  for (i=1; i<v->n; i++) if (!argused(v,i))
+  { v1 = findvar(argval(v,i),0,&n);
+    if (v==NULL)
+    { WARN(("variable %s not found; skipping",argval(v,i)));
+    }
+    else
+    { (*fs)(v1->name);
+      (*fn)(&v1->n,sizeof(INT));
+      (*fn)(&v1->mode,sizeof(INT)); /* mode indicator for later */
+      (*fn)(v1->dpr,v1->n*sizeof(double));
+    }
+    setused(v,i);
+  }
+  (*fs)("__end__");
+  closefile();
+}
+
+void doreaddata(name,fp)
+char *name;
+int fp;
+{ void (*fn)(), (*fs)();
+  INT i, k, md, n, of;
+  varname vn;
+  vari *v;
+
+  if (setfilename(name,"lfd","rb",fp)==0)
+  { ERROR(("readdata: cannot access file %s",filename));
+    return;
+  }
+  openfile("rb");
+  if (lf_error) return;
+  fn = readchar;
+  fs = readstr;
+
+  of = 0;
+  (*fn)(&i, sizeof(INT));
+  if (i!=LFDATAID) /* wrong or old format */
+  { if (i==-367)
+    { printf("Old format LFData file\n");
+      of = 1;
+    }
+    else
+    { ERROR(("not a Locfit data file: %s",name));
+    }
+  }
+  if (lf_error) { closefile(); return; }
+
+  if (of) /* old format: n nv name (10 char) data */
+  { (*fn)(&n,sizeof(INT));
+    (*fn)(&k,sizeof(INT));
+    for (i=0; i<k; i++)
+    { (*fn)(vn,10);
+      v = createvar(vn,STREGULAR,n,VDOUBLE);
+      (*fn)(v->dpr,n*sizeof(double));
+    }
+  }
+  else /* new format: name (str) n mode data __end__ */
+  { k = 999999;
+    for (i=0; i<k; i++)
+    { (*fs)(vn);
+      if (strcmp(vn,"__end__")==0) i=999999;
+      else
+      { (*fn)(&n,sizeof(INT));
+        (*fn)(&md,sizeof(INT));
+        v = createvar(vn,STREGULAR,n,md);
+        (*fn)(v->dpr,n*sizeof(double));
+  } } }
+  closefile();
+}
+
+#define FITID 4395943.3249934
+
+void dosavefit(lf,fi,mode,fp)
+lfit *lf;
+char *fi, *mode;
+int fp;
+{ void (*fn)();
+  double z;
+  INT d = 0, i, k, lm, ld;
+
+  if (fi==NULL) return;
+  if (setfilename(fi,"fit",mode,fp)==0)
+  { ERROR(("savefit: cannot access file %s.",fi));
+    return;
+  }
+
+  if (mode[0]=='r')
+    fn = readchar;
+  else
+  { if (lf->mi[MEV]==ENULL) ERROR(("savefit: No fit to save."));
+    if (lf_error) return;
+    fn = dumpchar;
+    z = FITID;
+    lm = LENM; ld = LEND;
+    d = lf->mi[MDIM];
+  }
+
+  openfile(mode);
+  (*fn)(&z,sizeof(double));
+
+  if ((mode[0]=='r') && (z!=FITID))
+  { ERROR(("readfit: file %s is not an evaluation structure",filename));
+    closefile();
+    return;
+  }
+
+  /* if reading, ensure lf.mi etc are assigned */
+  if (mode[0]=='r') fitdefault(lf,0,1);
+
+  (*fn)(&lm,sizeof(INT));
+  (*fn)(lf->mi,lm*sizeof(INT));
+  (*fn)(&ld,sizeof(INT));
+  (*fn)(lf->dp,LEND*sizeof(double));
+  (*fn)(&lf->nv,sizeof(INT));
+  (*fn)(&lf->nce,sizeof(INT));
+  (*fn)(&lf->vc,sizeof(INT));
+  (*fn)(&lf->nnl,sizeof(INT)); /* no longer used -- delete sometime! */
+
+  if (mode[0]=='r')
+  { d = lf->mi[MDIM];
+    trchck(lf,lf->nv,lf->nce,d,lf->mi[MP],lf->vc);
+    pcchk(&lf->pc,d,lf->mi[MP],1);
+    if ((mode[0]=='r') && (lm<20)) lf->mi[MPC] = 1-noparcomp(lf);
+  }
+  (*fn)(vdptr(lf->xxev),d*lf->nv*sizeof(double));
+  for (i=0; i<3*lf->mi[MDIM]+8; i++)
+    (*fn)(&lf->coef[i*lf->nvm],lf->nv*sizeof(double));
+
+  for (i=0; i<d; i++) (*fn)(lf->xname[i],10);
+  (*fn)(lf->yname,10);
+  (*fn)(lf->bname,10);
+  (*fn)(lf->cname,10);
+  (*fn)(lf->wname,10);
+
+  (*fn)(lf->sv,lf->nce*sizeof(double));
+  (*fn)(lf->fl,2*d*sizeof(double));
+  (*fn)(lf->sca,d*sizeof(double));
+  (*fn)(lf->ce,lf->nce*lf->vc*sizeof(INT));
+  (*fn)(lf->s,lf->nce*sizeof(INT));
+  k = 0;
+  if ((lf->mi[MEV]==EPHULL) | (lf->mi[MEV]==ETREE)) k = lf->nv;
+  if (lf->mi[MEV]==EKDTR) k = lf->nce;
+  (*fn)(lf->lo,k*sizeof(INT));
+  (*fn)(lf->hi,k*sizeof(INT));
+  (*fn)(lf->sty,d*sizeof(INT));
+  if (lf->mi[MEV]==EGRID)
+    (*fn)(lf->mg,d*sizeof(INT));
+  (*fn)(&lf->nd,sizeof(INT));
+  (*fn)(lf->deriv,lf->nd*sizeof(INT));
+
+  (*fn)(vdptr(lf->pc.wk),pc_reqd(d,lf->mi[MP])*sizeof(double));
+  lf->pc.xtwx.p = lf->mi[MP];
+/* MUST save lf->pc.xtwx.sm here */
+  lf->pc.xtwx.sm = lf->pc.xtwx.st = JAC_EIGD;
+
+  closefile();
+}
+
+#endif
diff --git a/src/locfit/lffuns.h b/src/locfit/lffuns.h
new file mode 100644
index 0000000..f4fe621
--- /dev/null
+++ b/src/locfit/lffuns.h
@@ -0,0 +1,215 @@
+
+
+
+/* FILES IN THE src DIRECTORY */
+
+/* adap.c */
+extern double afit(), aband2(), aband3();
+extern INT ainitband();
+
+/* band.c */
+extern void band(), kdeselect();
+
+/* density.c */
+extern INT densinit();
+extern INT fact[];
+extern int likeden();
+extern void prodint_resp(), prresp();
+
+/* dens_haz.c */
+extern void haz_init();
+extern INT hazint();
+
+/* dens_int.c */
+extern double dens_integrate();
+extern void dens_renorm(), dens_lscv(), lforder();
+
+/* dist.c */
+extern double igamma(), ibeta();
+extern double pf(), pchisq(), pnorm();
+extern double df(), dchisq();
+
+/* ev_atree.c */
+extern void atree_start(), atree_grow(), atree_guessnv();
+extern double atree_int();
+
+/* ev_interp.c */
+extern double dointpoint(), cubintd();
+extern double linear_interp(), cubic_interp(), rectcell_interp();
+extern INT exvval();
+extern void exvvalpv(), hermite2();
+
+/* ev_kdtre.c */
+extern void kdtre_start();
+extern double kdtre_int();
+
+/* ev_main.c */
+extern void trchck(), guessnv();
+extern void dataf(), gridf(), crossf(), xbarf(), preset();
+extern INT newsplit();
+extern int lfit_reqd(), lfit_reqi();
+#ifndef CVERSION
+extern vari *createvar();
+#endif
+
+/* ev_trian.c */
+extern void triang_start(), triang_grow();
+extern double triang_int();
+
+/* family.c */
+extern INT links(), stdlinks(), defaultlink(), validlinks();
+extern double b2(), b3(), b4(), lf_link(), invlink();
+
+/* frend.c */
+extern void fitfun(), degfree(), ressumm(), makecfn();
+extern INT procv(), procvraw(), procvvord();
+extern double base(), cens(), prwt(), resp(), getxi(), rss();
+extern INT calcp();
+
+/* kappa0.c */
+extern double critval(), critvalc(), tailp(), taild();
+extern INT constants();
+
+/* lf_dercor.c */
+extern void dercor();
+
+/* lf_fitfun.c */
+extern void fitfun(), designmatrix();
+extern INT calcp(), coefnumber();
+
+/* lf_robust.c */
+extern double median();
+extern void lf_robust();
+
+/* lfstr.c */
+extern void setstrval();
+extern INT ppwhat(), restyp();
+
+/* lf_vari.c */
+extern void comp_vari(), local_df();
+extern double comp_infl();
+
+/* linalg.c */
+extern void svd(), hsvdsolve();
+extern void addouter(), multmatscal();
+extern void QRupd(), QR1(), bacK(), bacT(), solve(), grsc();
+extern void setzero(), unitvec();
+extern void transpose();
+extern double innerprod(), m_trace();
+extern INT svdsolve();
+
+/* locfit.c or parfit.c (most) */
+extern int ident, locfit(), lf_iter();
+
+/* math.c */
+extern double lflgamma(), lferf(), lferfc(), lfdaws();
+extern double ptail(), logit(), expit();
+//extern double lgamma(), erf(), erfc();
+extern int factorial();
+
+/* minmax.c */
+extern double ipower(), minmax();
+
+/* nbhd.c */
+extern double kordstat(), nbhd(), rho();
+
+/* odint.c */
+extern INT onedint();
+extern void recurint();
+
+/* pcomp.c */
+extern double addparcomp();
+extern void compparcomp(), subparcomp(), subparcomp2(), pcchk();
+extern int pc_reqd();
+extern INT noparcomp(), hasparcomp();
+
+/* preplot.c */
+extern void preplot(), cpreplot();
+extern INT setpppoints();
+
+/* resid.c */
+extern double resid();
+extern void cfitted();
+extern vari *vfitted(), *vresid();
+
+/* scb.c */
+extern void scb(), cscbsim();
+
+/* simul.c */
+extern void liksim(), scbsim(), scbmax(), regband(), rband();
+
+/* startlf.c */
+extern void bbox(), deschk(), startlf(), preproc(), fitdefault();
+extern void fitoptions(), clocfit(), endfit();
+extern INT nofit();
+
+/* strings.c */
+extern int stm(), pmatch(), matchlf(), matchrt(), checkltor(), checkrtol();
+extern void strip();
+
+/* wdiag.c */
+extern INT wdiag(), procvhatm();
+extern void cwdiag();
+
+/* weight.c */
+extern double W(), weight(), weightd(), Wd(), Wdd(), wint();
+extern double Wconv(), Wconv1(), Wconv4(), Wconv5(), Wconv6(), Wikk();
+extern INT iscompact(), wtaylor();
+
+/* arith.c */
+extern INT arvect(), intitem();
+extern double areval(), arith(), darith(), dareval();
+extern vari *varith(), *saveresult(), *arbuild();
+
+/* c_args.c */
+#define argused(v,i) (((carg *)viptr(v,i))->used)
+#define setused(v,i) { ((carg *)viptr(v,i))->used = 1; }
+#define setunused(v,i) { ((carg *)viptr(v,i))->used = 0; }
+#define argarg(v,i) (((carg *)viptr(v,i))->arg)
+#define argvalis(v,i,z) (strcmp(argval(v,i),z)==0)
+extern char *argval(), *getargval();
+extern int getarg(), readilist(), getlogic();
+
+/* cmd.c */
+extern int locfit_dispatch(char*);
+extern void setuplf(), recondat(), cmdint();
+extern double backtr(), docrit();
+
+/* c_plot.c */
+extern void plotdata(), plotfit(), plottrack(), plotopt(), setplot();
+
+/* help.c */
+extern void example();
+
+/* lfd.c */
+extern void doreaddata(), dosavedata(), dosavefit();
+extern INT  setfilename();
+
+/* main.c */
+extern void SetWinDev();
+
+/* makecmd.c */
+extern vari *getcmd();
+extern void makecmd(), del_clines(), inc_forvar(), dec_forvar();
+
+/* post.c */
+extern void SetPSDev();
+
+/* pout.c */
+extern INT pretty();
+extern void displayplot();
+extern void plotmaple(), plotmathe(), plotmatlb(), plotgnup(), plotxwin();
+
+/* random.c */
+extern double rnorm(), rexp(), runif(), rpois();
+extern void rseed();
+
+/* readfile.c */
+extern void readfile();
+
+/* scbmax.c */
+extern void cscbmax();
+
+/* vari.c */
+#include "vari.hpp"
+
diff --git a/src/locfit/lfstr.c b/src/locfit/lfstr.c
new file mode 100644
index 0000000..ad5bf43
--- /dev/null
+++ b/src/locfit/lfstr.c
@@ -0,0 +1,150 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *
+ *  setstrval() is a  function for converting string arguments to Locfit's
+ *    numeric values.  A typical call will be setstrval(lf.mi,MKER,"gauss").
+ *
+ *  components that can be set in this manner are
+ *    MKER  (weight function)
+ *    MKT   (kernel type -- spherical or product)
+ *    MTG   (local likelihood family)
+ *    MLINK (link function)
+ *    MIT   (integration type for density estimation)
+ *    MEV   (evaluation structure)
+ *    MACRI (adaptive criterion)
+ *
+ *  INT ppwhat(str) interprets the preplot what argument.
+ *  INT restyp(str) interprets the residual type argument.
+ *
+ */
+
+#include "local.h"
+
+static char *famil[17] =
+  { "density", "ate",   "hazard",    "gaussian", "binomial",
+    "poisson", "gamma", "geometric", "circular", "obust", "huber",
+    "weibull", "cauchy","probab",    "logistic", "nbinomial", "vonmises" };
+static int   fvals[17] = 
+  { TDEN,  TRAT,  THAZ,  TGAUS, TLOGT,
+    TPOIS, TGAMM, TGEOM, TCIRC, TROBT, TROBT,
+    TWEIB, TCAUC, TPROB, TLOGT, TGEOM, TCIRC };
+
+INT lffamily(z)
+char *z;
+{ INT quasi, robu, f;
+  quasi = robu = 0;
+  while ((z[0]=='q') | (z[0]=='r'))
+  { quasi |= (z[0]=='q');
+    robu  |= (z[0]=='r');
+    z++;
+  }
+  f = pmatch(z,famil,fvals,16,-1);
+  if ((z[0]=='o') | (z[0]=='a')) robu = 0;
+  if (f==-1)
+  { WARN(("unknown family %s",z));
+    f = TGAUS;
+  }
+  if (quasi) f += 64;
+  if (robu)  f += 128;
+  return(f);
+}
+
+void getlffam(z,x)
+char **z;
+INT *x;
+{ *x = lffamily(z[0]);
+}
+
+static char *wfuns[13] = {
+  "rectangular", "epanechnikov", "bisquare",    "tricube",
+  "triweight",   "gaussian",     "triangular",  "ququ",
+  "6cub",        "minimax",      "exponential", "maclean", "parametric" };
+static int wvals[13] = { WRECT, WEPAN, WBISQ, WTCUB,
+  WTRWT, WGAUS, WTRIA, WQUQU, W6CUB, WMINM, WEXPL, WMACL, WPARM };
+
+static char *ktype[3] = { "spherical", "product", "center" };
+static int   kvals[3] = { KSPH, KPROD, KCE };
+
+static char *ltype[8] = { "default", "canonical", "identity", "log",
+                          "logi",    "inverse",   "sqrt",     "arcsin" };
+static int   lvals[8] = { LDEFAU, LCANON, LIDENT, LLOG,
+                          LLOGIT, LINVER, LSQRT,  LASIN };
+
+static char *etype[9] = { "tree",     "phull", "data", "grid", "kdtree",
+                          "kdcenter", "cross", "xbar", "none" };
+static int   evals[9] = { ETREE, EPHULL, EDATA, EGRID, EKDTR,
+                          EKDCE, ECROS,  EXBAR, ENONE };
+
+static char *itype[6] = { "default", "multi", "product", "mlinear",
+                          "hazard",  "monte" };
+static int   ivals[6] = { IDEFA, IMULT, IPROD, IMLIN, IHAZD, IMONT };
+
+static char *atype[5] = { "none", "cp", "ici", "mindex", "ok" };
+static int   avals[5] = { ANONE, ACP, AKAT, AMDI, AOK };
+
+void setstrval(mi,v,z)
+INT *mi, v;
+char *z;
+{ 
+  switch(v)
+  { case MKER:
+      mi[v] = pmatch(z, wfuns, wvals, 13, WTCUB);
+      return;
+
+    case MKT:
+      mi[v] = pmatch(z, ktype, kvals, 3, KSPH);
+      return;
+
+    case MTG:
+      mi[v] = lffamily(z);
+      return;
+
+    case MLINK:
+      mi[v] = pmatch(z, ltype, lvals, 8, LDEFAU);
+      return;
+
+    case MIT:
+      mi[v] = pmatch(z, itype, ivals, 6, IDEFA);
+      return;
+
+    case MEV:
+      mi[v] = pmatch(z, etype, evals, 9, ETREE);
+      return;
+
+    case MACRI:
+      mi[v] = pmatch(z, atype, avals, 5, ANONE);
+      return;
+  }
+
+  WARN(("setstrval: invalid value %d",v));
+  return;
+}
+
+static char *rtype[8] = { "deviance", "d2",    "pearson", "raw",
+                          "ldot",     "lddot", "fit",     "mean" };
+static int   rvals[8] = { RDEV, RDEV2, RPEAR, RRAW, RLDOT, RLDDT, RFIT, RMEAN};
+
+static char *whtyp[8] = { "coef", "nlx", "infl", "band",
+                          "degr", "like", "rdf", "vari" };
+static int   whval[8] = { PCOEF, PNLX, PT0, PBAND, PDEGR, PLIK, PRDF, PVARI };
+
+INT restyp(z)
+char *z;
+{ int val;
+  
+  val = pmatch(z, rtype, rvals, 8, -1);
+  if (val==-1) ERROR(("Unknown type = %s",z));
+  return((INT)val);
+}
+
+INT ppwhat(z)
+char *z;
+{ int val;
+  
+  val = pmatch(z, whtyp, whval, 8, -1);
+  if (val==-1) ERROR(("Unknown what = %s",z));
+  return((INT)val);
+}
diff --git a/src/locfit/lfstruc.h b/src/locfit/lfstruc.h
new file mode 100644
index 0000000..80caea6
--- /dev/null
+++ b/src/locfit/lfstruc.h
@@ -0,0 +1,102 @@
+/*
+ *   Copyright (c) 1998-2000 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *
+ *   Structures, typedefs etc used in Locfit
+ */
+
+typedef char varname[64];
+
+/*
+ *  Define the vari type for locfit variables and related macros.
+ *  For the C version, an enhanced vari type is needed;
+ *  for other versions a simple structure suffices.
+ */
+#ifdef CVERSION
+
+typedef struct {
+  varname name;
+  INT n, bytes, mode, stat;
+  double *dpr; } vari;
+#define checkvarlen(v,n,name,mode) (createvar(name,STSYSTEM,n,mode))
+#define vmode(v) ((v)->mode)
+
+#else
+
+typedef struct {
+  INT n;
+  double *dpr;
+} vari;
+#define viptr(v,i) (&(v)->dpr[i])
+#define checkvarlen(v,len,name,mode) \
+   ((((v)!=NULL) && (vlength(v) >= (len))) ? (v) : createvar((name),0,(len),(mode)))
+#endif
+
+#define vlength(v) ((v)->n)
+
+typedef struct {
+  char *arg, *val;
+  vari *result;
+  INT used; } carg;
+
+typedef struct {
+  void (*AddColor)(), (*SetColor)(), (*ClearScreen)(), (*TextDim)(), (*DoText)();
+  void (*DrawPoint)(), (*DrawLine)(), (*DrawPatch)(), (*wrapup)();
+  INT (*makewin)(), ticklength, defth, deftw;
+} device;
+
+typedef struct {
+  vari *wk;
+  double *coef, *xbar, *f;
+  jacobian xtwx; } paramcomp;
+
+typedef struct {
+  vari *tw, *L, *iw, *xxev;
+  double *x[MXDIM], *y, *w, *base, *c;
+  double *coef, *nlx, *t0, *lik, *h, *deg;
+  double *sv, dp[LEND], kap[3];
+  double sca[MXDIM], fl[2*MXDIM], xl[2*MXDIM];
+  INT *ce, *s, *lo, *hi, sty[MXDIM];
+  INT *mg, nvm, ncm, vc;
+  INT nl, nv, nnl, nce, nk, nn, mi[LENM], ord, deriv[MXDEG+2], nd;
+  paramcomp pc;
+  varname yname, xname[MXDIM], wname, bname, cname; } lfit;
+
+#define datum(lf,i,j) (lf)->x[i][j]
+#define dvari(lf,i)   (lf)->x[i]
+#define evpt(lf,i) (&(lf)->xxev->dpr[(i)*(lf)->mi[MDIM]])
+#define evptx(lf,i,k) ((lf)->xxev->dpr[(i)*(lf)->mi[MDIM]+(k)])
+
+typedef struct {
+  vari *data[MXDIM], *fit, *se;
+  INT d, wh, gr;
+} pplot;
+
+typedef struct {
+  char cmd;
+  double x, *v, (*f)();
+  INT m, nx[3];
+  vari *vv; } arstruct;
+
+typedef struct {
+  vari *x, *y, *z;
+  char type;
+  INT id, t, n, nx, ny, pch; } plxyz;
+
+typedef struct {
+  double theta, phi, xl[2], yl[2], zl[2], sl[10];
+  INT id, ty, nsl;
+  char main[50], xlab[50], ylab[50], zlab[50];
+  vari *track, *xyzs; } plots;
+
+#define PLNONE 0
+#define PLDATA 1
+#define PLFIT  2
+#define PLTRK  4
+
+struct lfcol {
+  char name[10];
+  INT n, r, g, b;
+};
diff --git a/src/locfit/lfwin.h b/src/locfit/lfwin.h
new file mode 100755
index 0000000..a96015e
--- /dev/null
+++ b/src/locfit/lfwin.h
@@ -0,0 +1,117 @@
+#define LFM_EXIT   0
+#define LFM_COPY   1
+#define LFM_PASTE  2
+#define LFM_RUN    3
+
+#define LFM_READA 10
+#define LFM_SAVED 11
+#define LFM_READD 12
+#define LFM_SUMD  13
+#define LFM_PLOTD 18
+
+#define LFM_LOCF  20
+#define LFM_READF 22
+#define LFM_SUMF  23
+#define LFM_PRFIT 24
+
+#define LFM_ALPH  70
+#define LFM_FIXH  71
+#define LFM_APEN  72
+#define LFM_DEG0  75
+#define LFM_DEG1  76
+#define LFM_DEG2  77
+#define LFM_DEG3  78
+
+#define LFM_ABOUT 81
+#define LFM_INDEX 82
+#define LFM_READM 83
+#define LFM_WWW   84
+
+#define LFP_ROT   10
+#define LFP_STY   11
+#define LFP_PS    42
+#define LFP_COL   13
+
+#define LFP_XLAB  20
+#define LFP_YLAB  21
+#define LFP_ZLAB  22
+#define LFP_MAIN  23
+
+#define AB_WWW 10
+
+#define CM_LINE 1
+#define CM_OK   99
+
+#define RL_ALP  0
+#define RL_ALPV 1
+#define RL_H    2
+#define RL_HV   3
+#define RL_PEN  4
+#define RL_PENV 5
+#define RL_DEG  10
+#define RL_FORM 20
+#define RL_FAMY 21
+#define RL_QUAS 22
+#define RL_ROBU 23
+#define RL_FIT  98
+#define RL_OK   99
+
+#define RP_VS 1
+#define RP_HS 2
+#define RP_AUT  3
+#define RP_DRAW 98
+#define RP_OK   99
+
+#define PD_X 1
+#define PD_Y 2
+#define PD_Z 3
+#define PD_DRAW 10
+#define PD_ADD  11
+#define PD_WIN  12
+
+#define PS_FIL 1
+#define PS_DR  8
+#define PS_CA  9
+#define PS_H 10
+#define PS_W 11
+
+#define SC_COL 1
+#define SC_SCO 2
+#define SC_DR  8
+#define SC_OK  9
+
+#define VN_VN 1
+#define VN_SA 2
+#define VN_RF 98
+#define VN_CA 99
+
+#define BP_ALP 1
+#define BP_ALV 2
+#define BP_AUT 3
+#define BP_FIT 4
+#define BP_EX 99
+
+#define GR_CM 10
+#define GR_ST 11
+
+#define LB_LAB  10
+#define LB_DRAW 11
+
+#define LD_QUIT 99
+
+/* about.c */
+extern void AboutDlg();
+
+/* devwin.c */
+extern void getwinsize(), GetFontInfo();
+
+/* dlgraph.c */
+extern void GStyleDlg(), LabelDlg(), PostDlg(), RotateDlg(), SetColDlg();
+
+/* winfile.c */
+extern void ReadFileDlg(), ReadDataDlg(), SaveDataDlg(), RunDlg();
+extern void ReadFitDlg();
+
+/* windlg.c */
+extern void BandDlg(), LocfitDlg(), PlotDataDlg(), wlocfit_dispatch();
+extern int LFDefDlgProc();
diff --git a/src/locfit/linalg.c b/src/locfit/linalg.c
new file mode 100644
index 0000000..3244538
--- /dev/null
+++ b/src/locfit/linalg.c
@@ -0,0 +1,337 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include "local.h"
+
+void svd(x,p,q,d,mxit)  /* svd of square matrix */
+double *x, *p, *q;
+INT d, mxit;
+{ INT i, j, k, iter, ms, zer;
+  double r, u, v, cp, cm, sp, sm, c1, c2, s1, s2, mx;
+  for (i=0; i<d; i++)
+    for (j=0; j<d; j++) p[i*d+j] = q[i*d+j] = (i==j);
+  for (iter=0; iter<mxit; iter++)
+  { ms = 0;
+    for (i=0; i<d; i++)
+      for (j=i+1; j<d; j++)
+      { mx = MAX(fabs(x[i*d+j]),fabs(x[j*d+i]));
+        zer = 1;
+        if (mx*mx>1.0e-15*fabs(x[i*d+i]*x[j*d+j]))
+        { if (fabs(x[i*(d+1)])<fabs(x[j*(d+1)]))
+          { for (k=0; k<d; k++)
+            { u = x[i*d+k]; x[i*d+k] = x[j*d+k]; x[j*d+k] = u;
+              u = p[k*d+i]; p[k*d+i] = p[k*d+j]; p[k*d+j] = u;
+            }
+            for (k=0; k<d; k++)
+            { u = x[k*d+i]; x[k*d+i] = x[k*d+j]; x[k*d+j] = u;
+              u = q[k*d+i]; q[k*d+i] = q[k*d+j]; q[k*d+j] = u;
+            }
+          }
+          cp = x[i*(d+1)]+x[j*(d+1)];
+          sp = x[j*d+i]-x[i*d+j];
+          r = sqrt(cp*cp+sp*sp);
+          if (r>0) { cp /= r; sp /= r; }
+              else { cp = 1.0; zer = 0;}
+          cm = x[i*(d+1)]-x[j*(d+1)];
+          sm = x[i*d+j]+x[j*d+i];
+          r = sqrt(cm*cm+sm*sm);
+          if (r>0) { cm /= r; sm /= r; }
+              else { cm = 1.0; zer = 0;}
+          c1 = cm+cp;
+          s1 = sm+sp;
+          r = sqrt(c1*c1+s1*s1);
+          if (r>0) { c1 /= r; s1 /= r; }
+              else { c1 = 1.0; zer = 0;}
+          if (fabs(s1)>ms) ms = fabs(s1);
+          c2 = cm+cp;
+          s2 = sp-sm;
+          r = sqrt(c2*c2+s2*s2);
+          if (r>0) { c2 /= r; s2 /= r; }
+              else { c2 = 1.0; zer = 0;}
+          for (k=0; k<d; k++)
+          { u = x[i*d+k]; v = x[j*d+k];
+            x[i*d+k] = c1*u+s1*v;
+            x[j*d+k] = c1*v-s1*u;
+            u = p[k*d+i]; v = p[k*d+j];
+            p[k*d+i] = c1*u+s1*v;
+            p[k*d+j] = c1*v-s1*u;
+          }
+          for (k=0; k<d; k++)
+          { u = x[k*d+i]; v = x[k*d+j];
+            x[k*d+i] = c2*u-s2*v;
+            x[k*d+j] = s2*u+c2*v;
+            u = q[k*d+i]; v = q[k*d+j];
+            q[k*d+i] = c2*u-s2*v;
+            q[k*d+j] = s2*u+c2*v;
+          }
+          if (zer) x[i*d+j] = x[j*d+i] = 0.0;
+          ms = 1;
+        }
+      }
+    if (ms==0) iter=mxit+10;
+  }
+  if (iter==mxit) WARN(("svd not converged"));
+  for (i=0; i<d; i++)
+    if (x[i*d+i]<0)
+    { x[i*d+i] = -x[i*d+i];
+      for (j=0; j<d; j++) p[j*d+i] = -p[j*d+i];
+    }
+}
+
+INT svdsolve(x,w,P,D,Q,d,tol) /* original X = PDQ^T; comp. QD^{-1}P^T x */
+double *x, *w, *P, *D, *Q, tol;
+INT d;
+{ INT i, j, rank;
+  double mx;
+  if (tol>0)
+  { mx = D[0];
+    for (i=1; i<d; i++) if (D[i*(d+1)]>mx) mx = D[i*(d+1)];
+    tol *= mx;
+  }
+  rank = 0;
+  for (i=0; i<d; i++)
+  { w[i] = 0.0;
+    for (j=0; j<d; j++) w[i] += P[j*d+i]*x[j];
+  }
+  for (i=0; i<d; i++)
+    if (D[i*d+i]>tol)
+    { w[i] /= D[i*(d+1)];
+      rank++;
+    }
+  for (i=0; i<d; i++)
+  { x[i] = 0.0;
+    for (j=0; j<d; j++) x[i] += Q[i*d+j]*w[j];
+  }
+  return(rank);
+}
+
+void hsvdsolve(x,w,P,D,Q,d,tol) /* original X = PDQ^T; comp. D^{-1/2}P^T x */
+double *x, *w, *P, *D, *Q, tol;
+INT d;
+{ INT i, j;
+  double mx;
+  if (tol>0)
+  { mx = D[0];
+    for (i=1; i<d; i++) if (D[i*(d+1)]>mx) mx = D[i*(d+1)];
+    tol *= mx;
+  }
+  for (i=0; i<d; i++)
+  { w[i] = 0.0;
+    for (j=0; j<d; j++) w[i] += P[j*d+i]*x[j];
+  }
+  for (i=0; i<d; i++) if (D[i*d+i]>tol) w[i] /= sqrt(D[i*(d+1)]);
+  for (i=0; i<d; i++) x[i] = w[i];
+}
+
+void QRupd(X,u,p,w,y)
+double *X, *u, *w, y;
+INT p;
+{ INT i, j;
+  double s, c, r, t;
+  for (i=0; i<p; i++)
+  { c = X[i*p+i]; s = u[i];
+    if (s!=0)
+    { r = sqrt(c*c+s*s);
+      c = c/r; s = s/r;
+      for (j=i; j<p; j++)
+      { t = c*X[i*p+j]+s*u[j];
+        u[j] = c*u[j]-s*X[i*p+j];
+        X[i*p+j] = t;
+      }
+      if (w!=NULL)
+      { t = c*w[i]+s*y;
+        y = c*y-s*w[i];
+        w[i] = t;
+      }
+    }
+  }
+}
+
+void QR1(X,n,p,w)   /* QR of X (n*p); take w for the ride, if not NULL */
+double *X, *w;
+INT n, p;
+{ INT i, j, k, mi;
+  double c, s, mx, nx, t;
+  for (j=0; j<p; j++)
+  { mi = j; mx = fabs(X[(p+1)*j]); nx = mx*mx;
+    for (i=j+1; i<n; i++)
+    { nx += X[p*i+j]*X[p*i+j];
+      if (fabs(X[p*i+j])>mx) { mi = i; mx = fabs(X[p*i+j]); }
+    }
+    for (i=0; i<p; i++)
+    { t = X[p*j+i]; X[p*j+i] = X[p*mi+i]; X[p*mi+i] = t; }
+    if (w != NULL)
+    { t = w[j]; w[j] = w[mi]; w[mi] = t; }
+    if (X[(p+1)*j]>0)
+    { for (i=j; i<p; i++) X[p*j+i] = -X[p*j+i];
+      if (w != NULL) w[j] = -w[j];
+    }
+    nx = sqrt(nx);
+    c = nx*(nx-X[(p+1)*j]);
+    if (c!=0)
+    { for (i=j+1; i<p; i++)
+      { s = 0;
+        for (k=j; k<n; k++)
+          s += X[p*k+i]*X[p*k+j];
+        s = (s-nx*X[p*j+i])/c;
+        for (k=j; k<n; k++)
+          X[p*k+i] -= s*X[p*k+j];
+        X[p*j+i] += s*nx;
+      }
+      if (w != NULL)
+      { s = 0;
+        for (k=j; k<n; k++)
+          s += w[k]*X[p*k+j];
+        s = (s-nx*w[j])/c;
+        for (k=j; k<n; k++)
+          w[k] -= s*X[p*k+j];
+        w[j] += s*nx;
+      }
+      X[p*j+j] = nx;
+   }
+} }
+
+void bacK(R,f,p)   /* R^{-1} f */
+double *R, *f;
+INT p;
+{ INT i, j;
+  for (i=p-1; i>=0; i--)
+  { for (j=i+1; j<p; j++)
+      f[i] -= R[i*p+j]*f[j];
+    f[i] /= R[i*(p+1)];
+  }
+}
+
+void bacT(R,f,p,i0,i1)   /* R^{-1} (R^T)^{-1} f; p columns; use i0-(i1-1) */
+double *R, *f;
+INT p;
+int i0;
+int i1;
+{ INT i, j;
+  for (i=i0; i<i1; i++)
+  { for (j=i0; j<i; j++)
+      f[i-i0] -= R[j*p+i]*f[j-i0];
+    f[i-i0] /= R[i*(p+1)];
+  }
+  for (i=i1-1; i>=i0; i--)
+  { for (j=i+1; j<i1; j++)
+      f[i-i0] -= R[i*p+j]*f[j-i0];
+    f[i-i0] /= R[i*(p+1)];
+  }
+}
+
+void solve(A,b,d) /* this is crude! A organized by column. */
+double *A, *b;
+INT d;
+{ INT i, j, k;
+  double piv;
+  for (i=0; i<d; i++)
+  { piv = A[(d+1)*i];
+    for (j=i; j<d; j++) A[j*d+i] /= piv;
+    b[i] /= piv;
+    for (j=0; j<d; j++) if (j != i)
+    { piv = A[i*d+j];
+      A[i*d+j] = 0;
+      for (k=i+1; k<d; k++)
+        A[k*d+j] -= piv*A[k*d+i];
+      b[j] -= piv*b[i];
+    }
+  }
+}
+
+void grsc(X,d)
+double *X;
+INT d;
+{ INT i, j, k;
+  double nx;
+  for (i=0; i<d; i++)
+  { nx = 0;
+    for (j=0; j<d; j++) nx += X[i*d+j]*X[i*d+j];
+    nx = sqrt(nx);
+    for (j=0; j<d; j++) X[i*d+j] /= nx;
+    for (j=i+1; j<d; j++)
+    { nx = 0;
+      for (k=0; k<d; k++) nx += X[i*d+k]*X[j*d+k];
+      for (k=0; k<d; k++) X[j*d+k] -= nx*X[i*d+k];
+    }
+  }
+}
+
+void setzero(v,p)
+double *v;
+INT p;
+{ int i;
+  for (i=0; i<p; i++) v[i] = 0.0;
+}
+
+void unitvec(x,k,p)
+double *x;
+INT k, p;
+{ setzero(x,p);
+  x[k] = 1.0;
+}
+
+double innerprod(v1,v2,p)
+double *v1, *v2;
+INT p;
+{ int i;
+  double s;
+  s = 0;
+  for (i=0; i<p; i++) s += v1[i]*v2[i];
+  return(s);
+}
+
+void addouter(re,v1,v2,p,c)
+double *re, *v1, *v2, c;
+INT p;
+{ int i, j;
+  for (i=0; i<p; i++)
+    for (j=0; j<p; j++)
+      re[i*p+j] += c*v1[i]*v2[j];
+}
+
+void multmatscal(A,z,n)
+double *A, z;
+INT n;
+{ INT i;
+  for (i=0; i<n; i++) A[i] *= z;
+}
+
+/*
+ *  transpose() transposes an m*n matrix in place.
+ *  At input, the matrix has n rows, m columns and
+ *    x[0..n-1] is the is the first column.
+ *  At output, the matrix has m rows, n columns and
+ *    x[0..m-1] is the first column.
+ */
+void transpose(x,m,n)
+double *x;
+INT m, n;
+{ INT t0, t, ti, tj;
+  double z;
+  for (t0=1; t0<m*n-2; t0++)
+    { ti = t0%m; tj = t0/m;
+      do
+      { t = ti*n+tj;
+        ti= t%m;
+        tj= t/m;
+      } while (t<t0);
+      z = x[t];
+      x[t] = x[t0];
+      x[t0] = z;
+    }
+}
+
+/* trace of an n*n square matrix. */
+double m_trace(x,n)
+double *x;
+int n;
+{ int i;
+  double sum;
+  sum = 0;
+  for (i=0; i<n; i++)
+    sum += x[i*(n+1)];
+  return(sum);
+}
diff --git a/src/locfit/local.h b/src/locfit/local.h
new file mode 100644
index 0000000..c30fa4b
--- /dev/null
+++ b/src/locfit/local.h
@@ -0,0 +1,148 @@
+/*
+ *   Copyright (c) 1996-2000 Lucent Technologies.
+ *   See README file for details.
+ * 
+ *
+ *  Most of the changes formerly needed here are handled through
+ *  the Makefiles and #ifdef's.
+ *
+ *
+ */
+
+#define CVERSION 1
+
+#ifndef I_LF_H
+#define I_LF_H
+
+/*RVERSION*/
+
+/*
+ *   DIRSEP: '/' for unix; '\\' for DOS
+ */
+#ifdef DOS
+#define DIRSEP '\\'
+#else
+#define DIRSEP '/'
+#endif
+
+
+/*
+   Some older math libraries have no lgamma() function, and gamma(arg)
+   actually returns log(gamma(arg)). If so, you need to change
+   LGAMMA macro below.
+
+   If all else fails, you can also use lflgamma().
+
+   Use the definitions for erf, erfc and daws only if your
+   math libraries don't include these functions.
+ */
+#ifdef DOS
+#define LGAMMA(arg) lflgamma(arg)
+#define erf(x) lferf(x)
+#define erfc(x) lferfc(x)
+#else
+#define LGAMMA(arg) lgamma(arg)
+#endif
+#define daws(x) lfdaws(x)
+
+
+/*
+   Does your system support popen() and pclose()
+   for pipes? For most flavours of unix, yes.
+   For other OS's, usually not, and you'll need to
+   uncomment the following line.
+
+   (This is only relevant if making the C version).
+*/
+/* #define NOPIPES */
+
+
+/*
+   (the #ifdef's below should now take care of this).
+   the INT type is used for all integers provided in .C() calls from S.
+   For the S version, this should be long int.
+   For the R version, should be int.
+   For the C version, either is adequate.
+   Usually this only makes a difference on 64 bit systems.
+*/
+
+//#ifndef SWINVERSION
+
+//#ifdef RVERSION
+//typedef int INT;
+//#else
+//#ifdef SVERSION
+//typedef long int INT;
+//#else
+typedef int INT;
+//#endif /* SVERSION */
+//#endif /* RVERSION */
+
+//#endif /* SWINVERSION */
+
+/******** NOTHING BELOW HERE NEEDS CHANGING **********/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#ifdef RVERSION
+#undef LGAMMA
+#define LGAMMA(arg) Rf_lgammafn(arg)
+extern double Rf_lgammafn();
+#define SVERSION
+#endif
+
+#ifdef SWINVERSION
+#define SVERSION
+#include "newredef.h"
+#endif
+
+#include "mutil.h"
+#include "lfcons.h"
+#include "lfstruc.h"
+#include "design.h"
+#include "lffuns.h"
+
+#ifdef CVERSION
+//#undef printf
+//#define printf lfprintf
+//extern int lfprintf(const char *format, ...);
+//extern int printf(const char *format, ...);
+#endif
+
+#ifdef SVERSION
+#define printf printf
+#endif
+
+#ifdef INTERFACE
+#define printf printf
+#endif
+
+#define ERROR(args) printf("Error: "), printf args , printf("\n"), lf_error=1
+#define WARN(args)  printf("Warning: "),printf args, printf("\n")
+
+#define MAX(a,b) (((a)>(b)) ? (a) : (b))
+#define MIN(a,b) (((a)<(b)) ? (a) : (b))
+#define SGN(x) (((x)>0) ? 1 : -1)
+#define SQR(x) ((x)*(x))
+#define NOSLN 0.1278433
+#define GFACT 2.5
+#define EFACT 3.0
+
+#define MAXCOLOR 20
+#define MAXWIN 5
+
+#ifdef SWINVERSION
+#define ISWAP(a,b) { int zz; zz = a; a = b; b = zz; }
+#else
+#define ISWAP(a,b) { INT zz; zz = a; a = b; b = zz; }
+extern INT lf_error;
+#endif
+
+extern INT lf_error;
+
+double lf_exp(double x);
+
+#endif /* I_LF_H */
diff --git a/src/locfit/locfit.c b/src/locfit/locfit.c
new file mode 100644
index 0000000..b62dffb
--- /dev/null
+++ b/src/locfit/locfit.c
@@ -0,0 +1,263 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include "local.h"
+
+static double s0, s1, tol;
+static lfit   *lf_lf;
+static design *lf_des;
+int lf_status;
+int ident=0;
+int (*like)();
+extern double robscale;
+
+int likereg(coef, lk0, f1, Z)
+double *coef, *lk0, *f1, *Z;
+{ INT i, ii, j, p, *mi;
+    double lk, ww, link[LLEN], *X;
+    lf_status = LF_OK;
+    lk = 0.0; p = lf_des->p;
+    mi = lf_lf->mi;
+    setzero(Z,p*p);
+    setzero(f1,p);
+    for (i=0; i<lf_des->n; i++)
+    { ii = lf_des->ind[i];
+        X = d_xi(lf_des,i);
+        lf_des->th[i] = base(lf_lf,ii)+innerprod(coef,X,p);
+        lf_status = stdlinks(link,lf_lf,ii,lf_des->th[i],robscale);
+        if (lf_status == LF_BADP)
+        { *lk0 = -1.0e300;
+            return(NR_REDUCE);
+        }
+        if (lf_error) lf_status = LF_ERR;
+        if (lf_status != LF_OK) return(NR_BREAK);
+        
+        ww = lf_des->w[i];
+        lk += ww*link[ZLIK];
+        for (j=0; j<p; j++)
+            f1[j] += X[j]*ww*link[ZDLL];
+        addouter(Z, X, X, p, ww*link[ZDDLL]);
+    }
+    if (mi[MDEB]>2) prresp(coef,Z,p);
+    if (mi[MDEB]>1) printf("  likelihood: %8.5f\n",lk);
+    *lk0 = lf_des->llk = lk;
+    
+    switch (lf_lf->mi[MTG]&63) /* parameter checks */
+    { case TGAUS: /* prevent iterations! */
+            if ((mi[MLINK]==LIDENT)&((mi[MTG]&128)==0)) return(NR_BREAK);
+            break;
+        case TPOIS:
+        case TGEOM:
+        case TWEIB:
+        case TGAMM:
+            if ((mi[MLINK]==LLOG) && (fabs(coef[0])>700))
+            { lf_status = LF_OOB;
+                return(NR_REDUCE);
+            }
+            if (lk > -1.0e-5*s0)
+            { lf_status = LF_PF;
+                return(NR_REDUCE);
+            }
+            break;
+        case TRBIN:
+        case TLOGT:
+            if (lk > -1.0e-5*s0)
+            { lf_status = LF_PF;
+                return(NR_REDUCE);
+            }
+            if (fabs(coef[0])>700)
+            { lf_status = LF_OOB;
+                return(NR_REDUCE);
+            }
+            break;
+    }
+    return(NR_OK);
+}
+
+INT robustinit(lf,des)
+lfit *lf;
+design *des;
+{ int i;
+    for (i=0; i<des->n; i++)
+        des->res[i] = resp(lf,des->ind[i])-base(lf,des->ind[i]);
+    des->cf[0] = median(des->res,des->n);
+    for (i=1; i<des->p; i++) des->cf[i] = 0.0;
+    tol = 1.0e-6;
+    return(LF_OK);
+}
+
+INT circinit(lf,des)
+lfit *lf;
+design *des;
+{ int i, ii;
+    double s0, s1;
+    s0 = s1 = 0.0;
+    for (i=0; i<des->n; i++)
+    { ii = des->ind[i];
+        s0 += des->w[i]*prwt(lf,ii)*sin(resp(lf,ii)-base(lf,ii));
+        s1 += des->w[i]*prwt(lf,ii)*cos(resp(lf,ii)-base(lf,ii));
+    }
+    des->cf[0] = atan2(s0,s1);
+    for (i=1; i<des->p; i++) des->cf[i] = 0.0;
+    tol = 1.0e-6;
+    return(LF_OK);
+}
+
+INT reginit(lf,des)
+lfit *lf;
+design *des;
+{ int i, ii;
+    double sb, link[LLEN];
+    s0 = s1 = sb = 0;
+    for (i=0; i<des->n; i++)
+    { ii = des->ind[i];
+        links(base(lf,ii),resp(lf,ii),lf->mi[MTG],LINIT,link,cens(lf,ii),prwt(lf,ii),1.0);
+        s1 += des->w[i]*link[ZDLL];
+        s0 += des->w[i]*prwt(lf,ii);
+        sb += des->w[i]*prwt(lf,ii)*base(lf,ii);
+    }
+    if (s0==0) return(LF_NOPT); /* no observations with W>0 */
+    setzero(des->cf,des->p);
+    tol = 1.0e-6*s0;
+    switch(lf->mi[MLINK])
+    { case LIDENT:
+            des->cf[0] = (s1-sb)/s0;
+            return(LF_OK);
+        case LLOG:
+            if (s1<=0.0)
+            { des->cf[0] = -1000;
+                return(LF_INFA);
+            }
+            des->cf[0] = log(s1/s0) - sb/s0;
+            return(LF_OK);
+        case LLOGIT:
+            if (s1<=0.0)
+            { des->cf[0] = -1000;
+                return(LF_INFA);
+            }
+            if (s1>=s0)
+            { des->cf[0] = +1000;
+                return(LF_INFA);
+            }
+            des->cf[0] = logit(s1/s0)-sb/s0;
+            return(LF_OK);
+        case LINVER:
+            if (s1<=0.0)
+            { des->cf[0] = 1000;
+                return(LF_INFA);
+            }
+            des->cf[0] = s0/s1-sb/s0;
+            return(LF_OK);
+        case LSQRT:
+            des->cf[0] = sqrt(s1/s0)-sb/s0;
+            return(LF_OK);
+        case LASIN:
+            des->cf[0] = asin(sqrt(s1/s0))-sb/s0;
+            return(LF_OK);
+        default:
+            ERROR(("reginit: invalid link %d",lf->mi[MLINK]));
+            return(LF_ERR);
+    }
+}
+
+int lfinit(lf,des)
+lfit *lf;
+design *des;
+{ 
+    //double u[MXDIM];
+    INT *mi;
+    
+    mi = lf->mi;
+    des->xtwx.sm = (mi[MDEG0]<mi[MDEG]) ? JAC_CHOL : JAC_EIGD;
+    
+    designmatrix(lf,des);
+    
+    like = likereg;
+    switch(mi[MTG]&63)
+    { case TDEN:
+        case TRAT:
+        case THAZ:
+            like = likeden;
+            tol = (mi[MLINK]==LLOG) ? 1.0e-6 : 0.0;
+            return(densinit(lf,des,des->h,des->cf,des->n));
+        case TCAUC:
+        case TROBT:
+            return(robustinit(lf,des));
+        case TCIRC:
+            return(circinit(lf,des));
+        default:
+            return(reginit(lf,des));
+    }
+}
+
+void lfiter(lf,des)
+lfit *lf;
+design *des;
+{ int err;
+    max_nr(like, des->cf, des->oc, des->res, des->f1,
+           &des->xtwx, (int)des->p, (int)lf->mi[MMXIT], tol, &err);
+    switch(err)
+    { case NR_OK: return;
+        case NR_NCON:
+            WARN(("max_nr not converged"));
+            return;
+        case NR_NDIV:
+            WARN(("max_nr reduction problem"));
+            return;
+    }
+    WARN(("max_nr return status %d",err));
+}
+
+int use_robust_scale(int tg)
+{ if ((tg&64)==0) return(0); /* not quasi - no scale */
+    if (((tg&128)==0) & (((tg&63)!=TROBT) & ((tg&63)!=TCAUC))) return(0);
+    return(1);
+}
+
+int locfit(lf,des,h,noit)
+lfit *lf;
+design *des;
+double h;
+int noit;
+{ int i, p;
+    
+    if (lf->mi[MDEB]>0)
+    { printf("locfit: ");
+        for (i=0; i<lf->mi[MDIM]; i++) printf(" %10.6f",des->xev[i]);
+        printf("  h = %8.5f\n",h);
+    }
+    
+    lf_lf  = lf;
+    lf_des = des;
+    des->h = h;
+    p = des->p;
+    
+    lf_status = lfinit(lf,des);
+    if (lf_status != LF_OK) return(lf_status);
+    
+    if (use_robust_scale((int)lf->mi[MTG]))
+        lf_robust(lf,des);
+    else
+    { robscale = 1.0;
+        lfiter(lf,des);
+    }
+    
+    if (lf_status == LF_OOB)
+        for (i=1; i<p; i++) des->cf[i] = 0.0;
+    
+    if ((lf->mi[MTG]&63)==TDEN) /* convert from rate to density */
+    { switch(lf->mi[MLINK])
+        { case LLOG:
+                des->cf[0] -= log(lf->dp[DSWT]);
+                break;
+            case LIDENT:
+                multmatscal(des->cf,1.0/lf->dp[DSWT],des->p);
+                break;
+            default: ERROR(("Density adjustment; invalid link"));
+        }
+    }
+    
+    return(lf_status);
+}
diff --git a/src/locfit/m_chol.c b/src/locfit/m_chol.c
new file mode 100644
index 0000000..b14f757
--- /dev/null
+++ b/src/locfit/m_chol.c
@@ -0,0 +1,72 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include <math.h>
+#include "mutil.h"
+
+void chol_dec(A,n)
+double *A;
+int n;
+{ int i, j, k;
+  for (j=0; j<n; j++)
+  { k = n*j+j;
+    for (i=0; i<j; i++) A[k] -= A[n*i+j]*A[n*i+j];
+    if (A[k]<=0)
+    { for (i=j; i<n; i++) A[n*j+i] = 0.0; }
+    else
+    { A[k] = sqrt(A[k]);
+      for (i=j+1; i<n; i++)
+      { for (k=0; k<j; k++)
+          A[n*j+i] -= A[n*k+i]*A[n*k+j];
+        A[n*j+i] /= A[n*j+j];
+      }
+    }
+  }
+  for (j=0; j<n; j++)
+    for (i=j+1; i<n; i++) A[n*i+j] = 0.0;
+}
+
+int chol_solve(A,v,p)
+double *A, *v;
+int p;
+{ int i, j;
+
+  for (i=0; i<p; i++)
+  { for (j=0; j<i; j++) v[i] -= A[j*p+i]*v[j];
+    v[i] /= A[i*p+i];
+  }
+  for (i=p-1; i>=0; i--)
+  { for (j=i+1; j<p; j++) v[i] -= A[i*p+j]*v[j];
+    v[i] /= A[i*p+i];
+  }
+  return(p);
+}
+
+int chol_hsolve(A,v,p)
+double *A, *v;
+int p;
+{ int i, j;
+
+  for (i=0; i<p; i++)
+  { for (j=0; j<i; j++) v[i] -= A[j*p+i]*v[j];
+    v[i] /= A[i*p+i];
+  }
+  return(p);
+}
+
+double chol_qf(A,v,p)
+double *A, *v;
+int p;
+{ int i, j;
+  double sum;
+ 
+  sum = 0.0;
+  for (i=0; i<p; i++)
+  { for (j=0; j<i; j++) v[i] -= A[j*p+i]*v[j];
+    v[i] /= A[i*p+i];
+    sum += v[i]*v[i];
+  }
+  return(sum);
+}
diff --git a/src/locfit/m_eigen.c b/src/locfit/m_eigen.c
new file mode 100644
index 0000000..e5c3aef
--- /dev/null
+++ b/src/locfit/m_eigen.c
@@ -0,0 +1,141 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include <math.h>
+#include "mutil.h"
+#define E_MAXIT 20
+#define E_TOL 1.0e-8
+#define SQR(x) ((x)*(x))
+
+double e_tol(D,p)
+double *D;
+int p;
+{ double mx;
+  int i;
+  if (E_TOL <= 0.0) return(0.0);
+  mx = D[0];
+  for (i=1; i<p; i++) if (D[i*(p+1)]>mx) mx = D[i*(p+1)];
+  return(E_TOL*mx);
+}
+
+void eig_dec(X,P,d)
+double *X, *P;
+int d;
+{ int i, j, k, iter, ms;
+  double c, s, r, u, v;
+
+  for (i=0; i<d; i++)
+    for (j=0; j<d; j++) P[i*d+j] = (i==j);
+
+  for (iter=0; iter<E_MAXIT; iter++)
+  { ms = 0;
+    for (i=0; i<d; i++)
+      for (j=i+1; j<d; j++)
+        if (SQR(X[i*d+j]) > 1.0e-15*fabs(X[i*d+i]*X[j*d+j]))
+        { c = (X[j*d+j]-X[i*d+i])/2;
+          s = -X[i*d+j];
+          r = sqrt(c*c+s*s);
+          c /= r;
+          s = sqrt((1-c)/2)*(2*(s>0)-1);
+          c = sqrt((1+c)/2);
+          for (k=0; k<d; k++)
+          { u = X[i*d+k]; v = X[j*d+k];
+            X[i*d+k] = u*c+v*s;
+            X[j*d+k] = v*c-u*s;
+          }
+          for (k=0; k<d; k++)
+          { u = X[k*d+i]; v = X[k*d+j];
+            X[k*d+i] = u*c+v*s;
+            X[k*d+j] = v*c-u*s;
+          }
+          X[i*d+j] = X[j*d+i] = 0.0;
+          for (k=0; k<d; k++)
+          { u = P[k*d+i]; v = P[k*d+j];
+            P[k*d+i] = u*c+v*s;
+            P[k*d+j] = v*c-u*s;
+          }
+          ms = 1;
+        }
+    if (ms==0) return;
+  }
+  //printf("eig_dec not converged\n");
+}
+
+int eig_solve(J,x)
+jacobian *J;
+double *x;
+{ int d, i, j, rank;
+  double  *D, *P, *Q, *w;
+  double tol;
+
+  D = J->Z;
+  P = Q = J->Q;
+  d = J->p;
+  w = J->wk;
+
+  tol = e_tol(D,d);
+
+  rank = 0;
+  for (i=0; i<d; i++)
+  { w[i] = 0.0;
+    for (j=0; j<d; j++) w[i] += P[j*d+i]*x[j];
+  }
+  for (i=0; i<d; i++)
+    if (D[i*d+i]>tol)
+    { w[i] /= D[i*(d+1)];
+      rank++;
+    }
+  for (i=0; i<d; i++)
+  { x[i] = 0.0;
+    for (j=0; j<d; j++) x[i] += Q[i*d+j]*w[j];
+  }
+  return(rank);
+}
+
+int eig_hsolve(J,v)
+jacobian *J;
+double *v;
+{ int i, j, p, rank;
+  double *D, *Q, *w;
+  double tol;
+
+    rank = 0;
+  D = J->Z;
+  Q = J->Q;
+  p = J->p;
+  w = J->wk;
+
+  tol = e_tol(D,p);
+
+  for (i=0; i<p; i++)
+  { w[i] = 0.0;
+    for (j=0; j<p; j++) w[i] += Q[j*p+i]*v[j];
+  }
+  for (i=0; i<p; i++)
+    if (D[i*p+i]>tol)
+    { w[i] /= sqrt(D[i*(p+1)]);
+      rank++;
+    }
+  return(rank);
+}
+
+double eig_qf(J,v)
+jacobian *J;
+double *v;
+{ int i, j, p;
+  double sum, tol;
+
+  p = J->p;
+  sum = 0.0;
+  tol = e_tol(J->Z,p);
+
+  for (i=0; i<p; i++)
+    if (J->Z[i*p+i]>tol)
+    { J->wk[i] = 0.0;
+      for (j=0; j<p; j++) J->wk[i] += J->Q[j*p+i]*v[j];
+      sum += J->wk[i]*J->wk[i]/J->Z[i*p+i];
+    }
+  return(sum);
+}
diff --git a/src/locfit/m_jacob.c b/src/locfit/m_jacob.c
new file mode 100644
index 0000000..2b6e8c2
--- /dev/null
+++ b/src/locfit/m_jacob.c
@@ -0,0 +1,118 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include <stdlib.h>
+#include "math.h"
+#include "stdio.h"
+#include "mutil.h"
+
+#define DEF_METH JAC_EIGD
+
+int jac_reqd(int p) { return(2*p*(p+1)); }
+
+double *jac_alloc(J,p,wk)
+jacobian *J;
+int p;
+double *wk;
+{ if (wk==NULL)
+    wk = (double *)calloc(2*p*(p+1),sizeof(double));
+  J->Z = wk; wk += p*p;
+  J->Q = wk; wk += p*p;
+  J->wk= wk; wk += p;
+  J->dg= wk; wk += p;
+  return(wk);
+}
+
+void jacob_dec(J, meth)
+jacobian *J;
+int meth;
+{ int i, j, p;
+
+  if (J->st != JAC_RAW) return;
+
+  J->sm = J->st = meth;
+  switch(meth)
+  { case JAC_EIG:
+      eig_dec(J->Z,J->Q,J->p);
+      return;
+    case JAC_EIGD:
+      p = J->p;
+      for (i=0; i<p; i++)
+        J->dg[i] = (J->Z[i*(p+1)]<=0) ? 0.0 : 1/sqrt(J->Z[i*(p+1)]);
+      for (i=0; i<p; i++)
+        for (j=0; j<p; j++)
+          J->Z[i*p+j] *= J->dg[i]*J->dg[j];
+      eig_dec(J->Z,J->Q,J->p);
+      J->st = JAC_EIGD;
+      return;
+    case JAC_CHOL:
+      chol_dec(J->Z,J->p);
+      return;
+    default: printf("jacob_dec: unknown method %d",meth);
+  }
+}
+
+int jacob_solve(J,v) /* (X^T W X)^{-1} v */
+jacobian *J;
+double *v;
+{ int i, rank;
+
+  if (J->st == JAC_RAW) jacob_dec(J,DEF_METH);
+
+  switch(J->st)
+  { case JAC_EIG:
+      return(eig_solve(J,v));
+    case JAC_EIGD:
+      for (i=0; i<J->p; i++) v[i] *= J->dg[i];
+      rank = eig_solve(J,v);
+      for (i=0; i<J->p; i++) v[i] *= J->dg[i];
+      return(rank);
+    case JAC_CHOL:
+      return(chol_solve(J->Z,v,J->p));
+  }
+  printf("jacob_solve: unknown method %d",J->st);
+  return(0);
+}
+
+int jacob_hsolve(J,v) /*  J^{-1/2} v */
+jacobian *J;
+double *v;
+{ int i;
+
+  if (J->st == JAC_RAW) jacob_dec(J,DEF_METH);
+
+  switch(J->st)
+  { case JAC_EIG:
+      return(eig_hsolve(J,v));
+    case JAC_EIGD: /* eigenvalues on corr matrix */
+      for (i=0; i<J->p; i++) v[i] *= J->dg[i];
+      return(eig_hsolve(J,v));
+    case JAC_CHOL:
+      return(chol_hsolve(J->Z,v,J->p));
+  }
+  printf("jacob_hsolve: unknown method %d",J->st);
+  return(0);
+}
+
+double jacob_qf(J,v)  /* vT J^{-1} v */
+jacobian *J;
+double *v;
+{ int i;
+
+  if (J->st == JAC_RAW) jacob_dec(J,DEF_METH);
+
+  switch (J->st)
+  { case JAC_EIG:
+      return(eig_qf(J,v));
+    case JAC_EIGD:
+      for (i=0; i<J->p; i++) v[i] *= J->dg[i];
+      return(eig_qf(J,v));
+    case JAC_CHOL:
+      return(chol_qf(J->Z,v,J->p));
+    default:
+      printf("jacob_qf: invalid method\n");
+      return(0.0);
+  }
+}
diff --git a/src/locfit/m_max.c b/src/locfit/m_max.c
new file mode 100644
index 0000000..845207a
--- /dev/null
+++ b/src/locfit/m_max.c
@@ -0,0 +1,215 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *  Routines for maximization of a one dimensional function f()
+ *    over an interval [xlo,xhi]. In all cases. the flag argument
+ *    controls the return:
+ *      flag='x', the maximizer xmax is returned.
+ *                otherwise, maximum f(xmax) is returned.
+ *
+ *  max_grid(f,xlo,xhi,n,flag)
+ *    grid maximization of f() over [xlo,xhi] with n intervals.
+ *
+ *  max_golden(f,xlo,xhi,n,tol,err,flag)
+ *    golden section maximization.
+ *    If n>2, an initial grid search is performed with n intervals
+ *      (this helps deal with local maxima).
+ *    convergence criterion is |x-xmax| < tol.
+ *    err is an error flag.
+ *    if flag='x', return value is xmax.
+ *       otherwise, return value is f(xmax).
+ *
+ *  max_quad(f,xlo,xhi,n,tol,err,flag)
+ *    quadratic maximization.
+ *
+ *  max_nr()
+ *    newton-raphson, handles multivariate case.
+ *
+ *  TODO: additional error checking, non-convergence stop.
+ */
+
+#include <math.h>
+#include <string.h>
+#include "mutil.h"
+extern double innerprod();
+
+#define gold_rat 0.6180339887498948482045870
+#define max_val(x,y) ((flag=='x') ? x : y)
+
+double max_grid(f,xlo,xhi,n,flag)
+double (*f)(), xlo, xhi;
+int n;
+char flag;
+{ int i, mi;
+    mi = 0;
+    double x, y, mx, my;
+    my = 0.0;
+    for (i=0; i<=n; i++)
+    { x = xlo + (xhi-xlo)*i/n;
+        y = f(x);
+        if ((i==0) || (y>my))
+        { mx = x;
+            my = y;
+            mi = i;
+        }
+    }
+    if (mi==0) return(max_val(xlo,my));
+    if (mi==n) return(max_val(xhi,my));
+    return(max_val(mx,my));
+}
+
+double max_golden(f,xlo,xhi,n,tol,err,flag)
+double (*f)(), xhi, xlo, tol;
+int n, *err;
+char flag;
+{ double x0, x1, x2, x3, y0, y1, y2, y3;
+    *err = 0;
+    
+    if (n>2)
+    { x0 = max_grid(f,xlo,xhi,n,'x');
+        if (xlo<x0) xlo = x0-1.0/n;
+        if (xhi>x0) xhi = x0+1.0/n;
+    }
+    
+    x0 = xlo; y0 = f(xlo);
+    x3 = xhi; y3 = f(xhi);
+    x1 = gold_rat*x0 + (1-gold_rat)*x3; y1 = f(x1);
+    x2 = gold_rat*x3 + (1-gold_rat)*x1; y2 = f(x2);
+    
+    while (fabs(x3-x0)>tol)
+    { if ((y1>=y0) && (y1>=y2))
+    { x3 = x2; y3 = y2;
+        x2 = x1; y2 = y1;
+        x1 = gold_rat*x0 + (1-gold_rat)*x3; y1 = f(x1);
+    }
+    else if ((y2>=y3) && (y2>=y1))
+    { x0 = x1; y0 = y1;
+        x1 = x2; y1 = y2;
+        x2 = gold_rat*x3 + (1-gold_rat)*x1; y2 = f(x2);
+    }
+    else
+    { if (y3>y0) { x0 = x2; y0 = y2; }
+    else { x3 = x1; y3 = y1; }
+        x1 = gold_rat*x0 + (1-gold_rat)*x3; y1 = f(x1);
+        x2 = gold_rat*x3 + (1-gold_rat)*x1; y2 = f(x2);
+    }
+    }
+    if (y0>=y1) return(max_val(x0,y0));
+    if (y3>=y2) return(max_val(x3,y3));
+    return((y1>y2) ? max_val(x1,y1) : max_val(x2,y2));
+}
+
+double max_quad(f,xlo,xhi,n,tol,err,flag)
+double (*f)(), xhi, xlo, tol;
+int n, *err;
+char flag;
+{ double x0, x1, x2, xnew, y0, y1, y2, ynew, a, b;
+    *err = 0;
+    
+    if (n>2)
+    { x0 = max_grid(f,xlo,xhi,n,'x');
+        if (xlo<x0) xlo = x0-1.0/n;
+        if (xhi>x0) xhi = x0+1.0/n;
+    }
+    
+    x0 = xlo; y0 = f(x0);
+    x2 = xhi; y2 = f(x2);
+    x1 = (x0+x2)/2; y1 = f(x1);
+    
+    while (x2-x0>tol)
+    {
+        /* first, check (y0,y1,y2) is a peak. If not,
+         * next interval is the halve with larger of (y0,y2).
+         */
+        if ((y0>y1) | (y2>y1))
+        { 
+            if (y0>y2) { x2 = x1; y2 = y1; }
+            else { x0 = x1; y0 = y1; }
+            x1 = (x0+x2)/2;
+            y1 = f(x1);
+        }
+        else /* peak */
+        { a = (y1-y0)*(x2-x1) + (y1-y2)*(x1-x0);
+            b = ((y1-y0)*(x2-x1)*(x2+x1) + (y1-y2)*(x1-x0)*(x1+x0))/2;
+            /* quadratic maximizer is b/a. But first check if a's too
+             * small, since we may be close to constant.
+             */
+            if ((a<=0) | (b<x0*a) | (b>x2*a))
+            { /* split the larger halve */
+                xnew = ((x2-x1) > (x1-x0)) ? (x1+x2)/2 : (x0+x1)/2;
+            }
+            else
+            { xnew = b/a;
+                if (10*xnew < (9*x0+x1)) xnew = (9*x0+x1)/10;
+                if (10*xnew > (9*x2+x1)) xnew = (9*x2+x1)/10;
+                if (fabs(xnew-x1) < 0.001*(x2-x0))
+                {
+                    if ((x2-x1) > (x1-x0))
+                        xnew = (99*x1+x2)/100;
+                    else
+                        xnew = (99*x1+x0)/100;
+                }
+            }
+            ynew = f(xnew);
+            if (xnew>x1)
+            { if (ynew >= y1) { x0 = x1; y0 = y1; x1 = xnew; y1 = ynew; }
+            else { x2 = xnew; y2 = ynew; }
+            }
+            else
+            { if (ynew >= y1) { x2 = x1; y2 = y1; x1 = xnew; y1 = ynew; }
+            else { x0 = xnew; y0 = ynew; }
+            }
+        }
+    }
+    return(max_val(x1,y1));
+}
+
+double max_nr(F, coef, old_coef, f1, delta, J, p, maxit, tol, err)
+double *coef, *old_coef, *f1, *delta, tol;
+int (*F)(), p, maxit, *err;
+jacobian *J;
+{ double old_f, f, lambda;
+    int i, j, fr;
+    double nc, nd, cut;
+    int rank;
+    
+    *err = NR_OK;
+    J->p = p;
+    fr = F(coef, &f, f1, J->Z); J->st = JAC_RAW;
+    
+    for (i=0; i<maxit; i++)
+    { memcpy(old_coef,coef,p*sizeof(double));
+        old_f = f;
+        rank = jacob_solve(J,f1);
+        memcpy(delta,f1,p*sizeof(double));
+        
+        
+        if (rank==0) /* NR won't move! */
+            delta[0] = -f/f1[0];
+        
+        lambda = 1.0;
+        
+        nc = innerprod(old_coef,old_coef,p);
+        nd = innerprod(delta, delta, p);
+        cut = sqrt(nc/nd);
+        if (cut>1.0) cut = 1.0;
+        cut *= 0.0001;
+        do
+        { for (j=0; j<p; j++) coef[j] = old_coef[j] + lambda*delta[j];
+            fr = F(coef, &f, f1, J->Z); J->st = JAC_RAW;
+            if (fr==NR_BREAK) return(f);
+            
+            lambda = (fr==NR_REDUCE) ? lambda/2 : lambda/10.0;
+        } while ((lambda>cut) & (f <= old_f - 1.0e-3));
+        
+        if (f < old_f - 1.0e-3) { *err = NR_NDIV; return(f); }
+        if (fr==NR_REDUCE) return(f);
+        
+        if (fabs(f-old_f) < tol) return(f);
+        
+    }
+    *err = NR_NCON;
+    return(f);
+}
diff --git a/src/locfit/makecmd.c b/src/locfit/makecmd.c
new file mode 100644
index 0000000..12ced82
--- /dev/null
+++ b/src/locfit/makecmd.c
@@ -0,0 +1,256 @@
+/*
+ *   Copyright (c) 1996-2000 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *   The makecmd() function converts a command line string
+ *   into a locfit command variable. If the line has no
+ *   commands (for example, a blank line or a comment)
+ *   it returns NULL; otherwise, it returns the pointer to
+ *   the command variable.
+ *
+ *   The command line is split into arguments, with arguments
+ *   separated by spaces. Exception: A space in a quoted
+ *   "str ing" is not split into separate fields.
+ *
+ *   getcmd() returns a pointer to the next line for processing.
+ *     If no lines are ready for processing, it returns NULL.
+ *
+ *   del_lines()  frees the work space used by processed command lines.
+ *
+ *   set_forvar(), inc_forvar(), dec_forvar() are used in the
+ *     control of for loops.
+ */
+
+#include "local.h"
+
+#define isterminator(c) (((c)=='\0') | ((c)=='\n') | ((c)=='#'))
+static int clcount = 0;
+static int proc_to = 0;
+static int del_to = 0;
+extern vari *curstr;
+extern char filename[];
+
+typedef struct {
+  vari *v;
+  char *name;
+  int line_no;
+  int index; } forvar;
+static forvar fv[10];
+static int for_level = 0, proc_level = 0;
+
+int countfields(z)
+char *z;
+{ int n, instr;
+
+  n = 0;
+  instr = 0;
+
+  while (1)
+  { while (*z==' ') z++;
+    if (isterminator(*z)) return(n);
+
+    n++;
+
+    while ((instr) || (*z!=' '))
+    { if (isterminator(*z))
+      {  if (instr) ERROR(("Unterminated String"));
+         return(n);
+      }
+      if (*z=='"') instr = !instr;
+      z++;
+    }
+  }
+}
+
+void makefields(z, va, n)
+char *z;
+vari *va;
+int n;
+{ int i, instr;
+  char *st, *eq;
+  carg *curr_arg;
+
+  instr = 0;
+  for (i=0; i<n; i++)
+  { while (*z==' ') z++;
+
+    curr_arg = (carg *)viptr(va,i);
+    curr_arg->val = st = z;
+    curr_arg->arg = NULL;
+    curr_arg->result = NULL;
+
+    eq = NULL;
+    do
+    { if (*z=='"') instr = !instr;
+      if ((eq==NULL) && (!instr) && (*z=='=')) eq = z;
+      z++;
+    } while ((instr) || (*z !=' ') && (!isterminator(*z)));
+    *z = '\0';
+
+    if (eq != NULL)
+    { if (eq==st)
+      { ERROR(("command line argument begins with ="));
+        return;
+      }
+      if ((eq[1]!='=') & (strchr("<>!",eq[-1])==NULL))
+      { curr_arg->arg = st;
+        curr_arg->val = &eq[1];
+        *eq = '\0';
+      }
+    } /* eq != */
+    z++;
+
+  }   /* for i */
+}
+
+/*
+ *  set_forvar and inc_forvar are used to control for loops.
+ *  set_forvar is called when the for cmd is built, making the
+ *    variable to loop through.
+ *  inc_forvar is called when the for is processed, to update
+ *    the value of the variable.
+ *  dec_forvar is called when the endfor is processed. This
+ *    resets the proc_to line count to the beginning of the for loop.
+ */
+void set_forvar(v,ct)
+vari *v;
+int ct;
+{ 
+  varname vn;
+
+  if (vlength(v)<2)
+  { ERROR(("for: missing variable"));
+    return;
+  }
+
+  sprintf(vn,"=forv%d",for_level);
+  fv[for_level].v = varith(argval(v,1),vn,STHIDDEN);
+  fv[for_level].line_no = ct;
+  fv[for_level].index = 0;
+  fv[for_level].name = argarg(v,1);
+  for_level++;
+}
+
+void inc_forvar()
+{ varname vn;
+  vari *v, *va;
+  double x;
+  
+  if (fv[proc_level].name == NULL)
+  { sprintf(vn,"=fv%d",proc_level);
+    v = createvar(vn,STHIDDEN,1,VDOUBLE);
+  }
+  else
+    v = createvar(fv[proc_level].name,STREGULAR,1,VDOUBLE);
+
+  va = fv[proc_level].v;
+  x = vitem(va, fv[proc_level].index);
+  vassn(v,0,x);
+  fv[proc_level].index++;
+  proc_level++;
+}
+
+void dec_forvar()
+{ proc_level--;
+  if (fv[proc_level].index < vlength(fv[proc_level].v))
+    proc_to = fv[proc_level].line_no - 1;
+  else
+    fv[proc_level].index = 0;
+}
+
+void run(va)
+vari *va;
+{ FILE *runf;
+  char cline[256], *z;
+
+  if (vlength(va)<2)
+  { ERROR(("run: no command file"));
+    return;
+  }
+
+  if (!setfilename(argval(va,1),"","r",0))
+  { ERROR(("run: cannot read file %s",argval(va,1)));
+    return;
+  }
+
+  runf = fopen(filename,"r");
+  while (1)
+  { z = fgets(cline,256,runf);
+    if (z==NULL)
+    { fclose(runf);
+      return;
+    }
+    makecmd(cline);
+  }
+}
+
+void makecmd(cmdline)
+char *cmdline;
+{ 
+  varname vn;
+  vari *va, *vs;
+  int n;
+
+  n = countfields(cmdline);
+  if (lf_error) return;
+  if (n==0) return;
+  clcount++;
+
+  /* vs is used to store the command line string. */
+  sprintf(vn,"=clstr%d",clcount);
+  vs = createvar(vn,STSYSTEM,1+strlen(cmdline),VCHAR);
+  sprintf((char *)vdptr(vs),cmdline);
+
+  /* va is used to store pointers to the command line fields. */
+  sprintf(vn,"=cline%d",clcount);
+  va = createvar(vn,STSYSTEM,(INT)n,VARGL);
+  makefields((char *)vdptr(vs), va, n);
+
+  if (argvalis(va,0,"for")) set_forvar(va,clcount);
+  if (argvalis(va,0,"endfor")) for_level--;
+
+/* we want to read in run files here, not when commands are executed.
+ * otherwise, we would have problems with run commands in a for loop.
+ */
+  if (argvalis(va,0,"run")) run(va);
+
+  return;
+}
+
+void del_lines()
+{ int i;
+  varname vn;
+  for (i=proc_to; i>del_to; i--)
+  { sprintf(vn,"=cline%d",i);
+    deletename(vn);
+    sprintf(vn,"=clstr%d",i);
+    deletename(vn);
+  }
+  del_to = proc_to;
+}
+
+vari *getcmd()
+{
+  varname vn;
+  vari *v;
+
+  if (for_level > 0) return(NULL);
+
+  if (proc_to < clcount)
+  { 
+    sprintf(vn,"=cline%d",++proc_to);
+    v = findvar(vn,1,NULL);
+    if (v==NULL) return(v);
+
+/* this nonsense is req'd by setplot and setdef.
+ * get rid of it, I hope.
+ */
+sprintf(vn,"=clstr%d",proc_to);
+curstr = findvar(vn,1,NULL);
+
+    return(v);
+  }
+
+  return(NULL);
+}
diff --git a/src/locfit/math.c b/src/locfit/math.c
new file mode 100644
index 0000000..a420542
--- /dev/null
+++ b/src/locfit/math.c
@@ -0,0 +1,158 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+  miscellaneous functions that may not be defined in the math
+  libraries. The implementations are crude.
+  lflgamma(x) -- log(gamma(x))
+  lferf(x)    -- erf(x)
+  lferfc(x)   -- erfc(x)
+  lfdaws(x)   -- dawson's function
+
+  where required, these must be #define'd in local.h.
+
+  also includes
+  ptail(x)    -- exp(x*x/2)*int_{-\infty}^x exp(-u^2/2)du for x < -6.
+  logit(x)    -- logistic function.
+  expit(x)    -- inverse of logit.
+ */
+
+#include "local.h"
+
+double lferfc();
+
+double lferf(x)
+double x;
+{ static double val[] = { 0.0, 0.52049987781304674,
+    0.84270079294971501, 0.96610514647531076, 0.99532226501895282,
+    0.99959304798255499, 0.99997790950300125 };
+  double h, xx, y, z, f0, f1, f2;
+  int m, j;
+  if (x<0) return(-lferf(-x));
+  if (x>3.2) return(1-lferfc(x));
+  m = (int) (2*x+0.5);
+  xx= ((double)m)/2;
+  h = x-xx; y = h;
+  f0 = val[m];
+  f1 = 2*exp(-xx*xx)/SQRPI;
+  z = f0+h*f1;
+  j = 0;
+  while (fabs(y)>1.0e-12)
+  { f2 = -2*j*f0-2*xx*f1;
+    f0 = f1; f1 = f2;
+    y *= h/(j+2);
+    z += y*f2;
+    j++;
+  }
+  return(z);
+}
+
+double lferfc(x)
+double x;
+{ if (x<0) return(1+lferf(-x));
+  if (x<2.5) return(1-lferf(x));
+  return(exp(-x*x)/(x*SQRPI));
+}
+
+double lflgamma(x)
+double x;
+{ double x1;
+  static double ilg[] = { 0.0, 0.0, 0.69314718055994529,
+    1.791759469228055, 3.1780538303479458, 4.7874917427820458, 6.5792512120101012,
+    8.5251613610654147, 10.604602902745251, 12.801827480081469 };
+  static double hlg[] = { 0.57236494292470008, -0.12078223763524520,
+    0.28468287047291918, 1.20097360234707430,  2.45373657084244230,
+    3.95781396761871650, 5.66256205985714270,  7.53436423675873360,
+    9.54926725730099870, 11.68933342079726900 };
+
+  if (x<=0.0) return(0.0);
+  if (x<10)
+  { if (x==(int)x) return(ilg[(int)x-1]);
+    if ((x-0.5)==(int)(x-0.5)) return(hlg[(int)(x-0.5)]);
+  }
+  if (x<3) return(lflgamma(x+1)-log(x));
+
+  x1 = x-1;
+  return(HL2PI+(x1+0.5)*log(x1)-x1+1/(12*x1));
+}
+
+double lfdaws(x)
+double x;
+{ static double val[] = {
+  0,  0.24485619356002, 0.46034428261948, 0.62399959848185, 0.72477845900708,
+      0.76388186132749, 0.75213621001998, 0.70541701910853, 0.63998807456541,
+      0.56917098836654, 0.50187821196415, 0.44274283060424, 0.39316687916687,
+      0.35260646480842, 0.31964847250685, 0.29271122077502, 0.27039629581340,
+      0.25160207761769, 0.23551176224443, 0.22153505358518, 0.20924575719548,
+      0.19833146819662, 0.18855782729305, 0.17974461154688, 0.17175005072385 };
+  double h, f0, f1, f2, y, z, xx;
+  int j, m;
+  if (x<0) return(-daws(-x));
+  if (x>6)
+  { /* Tail series: 1/x + 1/x^3 + 1.3/x^5 + 1.3.5/x^7 + ...  */
+    y = z = 1/x;
+    j = 0;
+    while (((f0=(2*j+1)/(x*x))<1) && (y>1.0e-10*z))
+    { y *= f0;
+      z += y;
+      j++;
+    }
+    return(z);
+  }
+  m = (int) (4*x);
+  h = x-0.25*m;
+  if (h>0.125)
+  { m++;
+    h = h-0.25;
+  }
+  xx = 0.25*m;
+  f0 = val[m];
+  f1 = 1-xx*f0;
+  z = f0+h*f1;
+  y = h;
+  j = 2;
+  while (fabs(y)>z*1.0e-10)
+  { f2 = -(j-1)*f0-xx*f1;
+    y *= h/j;
+    z += y*f2;
+    f0 = f1; f1 = f2;
+    j++;
+  }
+  return(z);
+}
+
+double ptail(x) /* exp(x*x/2)*int_{-\infty}^x exp(-u^2/2)du for x < -6 */
+double x;
+{ double y, z, f0;
+  int j;
+  y = z = -1.0/x;
+  j = 0;
+  while ((fabs(f0= -(2*j+1)/(x*x))<1) && (fabs(y)>1.0e-10*z))
+  { y *= f0;
+    z += y;
+    j++;
+  }
+  return(z);
+}
+
+double logit(x)
+double x;
+{ return(log(x/(1-x)));
+}
+
+double expit(x)
+double x;
+{ double u;
+  if (x<0)
+  { u = exp(x);
+    return(u/(1+u));
+  }
+  return(1/(1+exp(-x)));
+}
+
+int factorial(n)
+int n;
+{ if (n<=1) return(1.0);
+  return(n*factorial(n-1));
+}
diff --git a/src/locfit/minmax.c b/src/locfit/minmax.c
new file mode 100644
index 0000000..1dd7ec3
--- /dev/null
+++ b/src/locfit/minmax.c
@@ -0,0 +1,302 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *   Compute minimax weights for local regression.
+ */
+
+#include "local.h"
+
+int mmsm_ct;
+
+static int debug=0;
+#define CONVTOL 1.0e-8
+#define SINGTOL 1.0e-10
+#define NR_SINGULAR 100
+
+static lfit *mm_lf;
+static design *mm_des;
+static double mm_gam;
+
+double ipower(x,n) /* use for n not too large!! */
+double x;
+int n;
+{ if (n==0) return(1.0);
+    if (n<0) return(1/ipower(x,-n));
+    return(x*ipower(x,n-1));
+}
+
+double setmmwt(des,lf,a,gam)
+design *des;
+lfit *lf;
+double *a, gam;
+{ double ip, w0, w1, sw, wt;
+    INT i, p;
+    sw = 0.0;
+    p = lf->mi[MP];
+    for (i=0; i<lf->mi[MN]; i++)
+    { ip = innerprod(a,d_xi(des,i),p);
+        wt = prwt(lf,i);
+        w0 = ip - gam*des->wd[i];
+        w1 = ip + gam*des->wd[i];
+        des->w[i] = 0.0;
+        if (w0>0) { des->w[i] = w0; sw += wt*w0*w0; }
+        if (w1<0) { des->w[i] = w1; sw += wt*w1*w1; }
+    }
+    return(sw/2-a[0]);
+}
+
+/* compute sum_{w!=0} AA^T; e1-sum wA  */
+int mmsums(coef,f,z,J)
+double *coef, *f, *z;
+jacobian *J;
+{ int i, j, p, sing;
+    double *A;
+    
+    mmsm_ct++;
+    A = J->Z;
+    *f = setmmwt(mm_des,mm_lf,coef,mm_gam);
+    
+    p = mm_lf->mi[MP];
+    setzero(A,p*p);
+    setzero(z,p);
+    z[0] = 1.0;
+    
+    for (i=0; i<mm_lf->mi[MN]; i++)
+        if (mm_des->w[i]!=0.0)
+        { addouter(A,d_xi(mm_des,i),d_xi(mm_des,i),p,prwt(mm_lf,i));
+            for (j=0; j<p; j++) z[j] -= prwt(mm_lf,i)*mm_des->w[i]*mm_des->X[i*p+j];
+        }
+    
+    J->st = JAC_RAW;
+    jacob_dec(J,JAC_EIGD);
+    
+    sing = 0;
+    for (i=0; i<p; i++) sing |= (J->Z[i*p+i]<SINGTOL);
+    if ((debug) & (sing)) printf("SINGULAR!!!!\n");
+    
+    /* printf("%8.5f %8.5f  %8.5f   f %8.5f  z %8.5f %8.5f\n",
+     coef[0],coef[1],mm_gam,*f,z[0],z[1]); */
+    return((sing) ? NR_SINGULAR : NR_OK);
+}
+
+double updatesd(des,lf,z,p,a,a0,sw0,gam)
+design *des;
+lfit *lf;
+int p;
+double *z, *a, *a0, sw0, gam;
+{ double f, sw, c0, c1, tmp[10];
+    INT i, j, sd;
+    
+    if (debug) printf("updatesd\n");
+    for (i=0; i<p; i++) if (des->xtwx.Z[i*p+i]<SINGTOL) sd = i;
+    if (des->xtwx.dg[sd]>0)
+        for (i=0; i<p; i++) tmp[i] = des->xtwx.Q[p*i+sd]*des->xtwx.dg[i];
+    else
+    { for (i=0; i<p; i++) tmp[i] = 0.0;
+        tmp[sd] = 1.0;
+    }
+    
+    mmsums(a0,&sw,z,&des->xtwx);
+    
+    c0 = c1 = 0.0;
+    for (i=0; i<p; i++)
+    { c0 += tmp[i]*z[i];
+        for (j=0; j<p; j++)
+            c1 += tmp[i]*des->xtwx.Z[i*p+j]*tmp[j];
+    }
+    if (debug) printf("sdir: c0 %8.5f  c1 %8.5f  z %8.5f %8.5f  tmp %8.5f %8.5f\n",c0,c1,z[0],z[1],tmp[0],tmp[1]);
+    if (c0<0) for (i=0; i<p; i++) tmp[i] = -tmp[i];
+    
+    f = 1.0;
+    for (i=0; i<p; i++) a[i] = a0[i]+tmp[i];
+    sw = setmmwt(des,lf,a,gam);
+    
+    if (sw<sw0) /* double till we drop */
+    { while(1)
+    { f *= 2;
+        sw0 = sw;
+        for (i=0; i<p; i++) a[i] = a0[i]+f*tmp[i];
+        sw = setmmwt(des,lf,a,gam);
+        if (sw>sw0-CONVTOL) /* go back one step */
+        { f /= 2;
+            for (i=0; i<p; i++) a[i] = a0[i]+f*tmp[i];
+            sw0 = setmmwt(des,lf,a,gam);
+            return(sw0);
+        }
+    }
+    }
+    
+    /* halve till success */
+    while (1)
+    { f *= 0.5;
+        for (i=0; i<p; i++) a[i] = a0[i]+f*tmp[i];
+        sw = setmmwt(des,lf,a,gam);
+        if (sw<sw0+CONVTOL) return(sw);
+    }
+}
+
+int mm_initial(des,lf,z,p,coef)
+design *des;
+lfit *lf;
+int p;
+double *z, *coef;
+{ int st;
+    double f;
+    
+    setzero(coef,p);
+    coef[0] = 1;
+    while (1)
+    {
+        st = mmsums(coef,&f,z,&des->xtwx);
+        if (st==NR_OK) return(0);
+        coef[0] *= 2;
+        if (coef[0]>1e8) return(1);
+    } 
+}
+
+int mmax(coef, old_coef, f1, delta, J, p, maxit, tol, err)
+double *coef, *old_coef, *f1, *delta, tol;
+int p, maxit, *err;
+jacobian *J;
+{ double f, old_f, lambda;
+    int i, j, fr, sing;
+    
+    *err = NR_OK;
+    J->p = p;
+    J->st = JAC_RAW;
+    fr = mmsums(coef,&f,f1,J);
+    
+    for (j=0; j<maxit; j++)
+    { memcpy(old_coef,coef,p*sizeof(double));
+        old_f = f;
+        
+        /* compute delta = Newton-Raphson increment */
+        /* jacob_dec(J,JAC_EIGD); */
+        
+        sing = (fr==NR_SINGULAR);
+        
+        if (fr == NR_SINGULAR)
+        { J->st = JAC_RAW;
+            if (j==0) printf("init singular\n");
+            f = updatesd(mm_des,mm_lf,delta,p,coef,old_coef,f,mm_gam);
+            fr = mmsums(coef,&f,f1,J);
+        }
+        else
+        { 
+            jacob_solve(J,f1);
+            memcpy(delta,f1,p*sizeof(double));
+            /* printf("delta %8.5f %8.5f\n",f1[0],f1[1]); */
+            lambda = 1.0;
+            do
+            {
+                for (i=0; i<p; i++) coef[i] = old_coef[i] + lambda*delta[i];
+                J->st = JAC_RAW;
+                fr = mmsums(coef,&f,f1,J);
+                
+                lambda = lambda/2.0;
+                /* if (fr==NR_SINGULAR) printf("singular\n"); */
+            } while (((lambda>0.000000001) & (f > old_f+0.001)) /* | (fr==NR_SINGULAR) */ );
+            
+            if (f>old_f+0.001) { printf("lambda prob\n"); *err = NR_NDIV; return(f); }
+            
+        }
+        if (f==0.0)
+        { if (sing) printf("final singular - conv\n");
+            return(f);
+        }
+        
+        if (debug)
+        { for (i=0; i<p; i++) printf("%8.5f ",coef[i]);
+            printf(" f %8.5f\n",f);
+        }
+        
+        if ((j>0) & (fabs(f-old_f)<tol)) return(f);
+    }
+    if (sing) printf("final singular\n");
+    WARN(("findab not converged"));
+    *err = NR_NCON;
+    return(f);
+}
+
+double findab(double gam)
+{ double *coef, sl;
+    int i, p, nr_stat;
+    
+    mm_gam = gam;
+    p = mm_lf->mi[MP];
+    
+    /* starting values for nr iteration */
+    coef = mm_des->cf;
+    for (i=0; i<p; i++) coef[i] = 0.0;
+    if (mm_initial(mm_des, mm_lf, mm_des->f1, p, coef))
+    { WARN(("findab: initial value divergence"));
+        return(0.0);
+    }
+    else
+        mmax(coef, mm_des->oc, mm_des->res, mm_des->f1,
+             &mm_des->xtwx, p, mm_lf->mi[MMXIT], CONVTOL, &nr_stat);
+    
+    if (nr_stat != NR_OK) return(0.0);
+    
+    sl = 0.0;
+    for (i=0; i<mm_lf->mi[MN]; i++) sl += fabs(mm_des->w[i])*mm_des->wd[i];
+    
+    return(sl-gam);
+}
+
+double weightmm(coef,di,ff,mi,gam)
+double *coef, di, *ff, gam;
+INT *mi;
+{ double y1, y2, ip;
+    ip = innerprod(ff,coef,mi[MP]);
+    y1 = ip-gam*di; if (y1>0) return(y1/ip);
+    y2 = ip+gam*di; if (y2<0) return(y2/ip);
+    return(0.0);
+}
+
+double minmax(lf,des)
+lfit *lf;
+design *des;
+{ double h, u[MXDIM], gam;
+    int i, j, m, d1, p1, err_flag;
+    
+    mmsm_ct = 0;
+    d1 = lf->mi[MDEG]+1;
+    p1 = factorial(d1);
+    for (i=0; i<lf->mi[MN]; i++)
+    { for (j=0; j<lf->mi[MDIM]; j++) u[j] = datum(lf,j,i);
+        des->wd[i] = lf->dp[DALP]/p1*ipower(des->di[i],d1);
+        des->ind[i] = i;
+        fitfun(lf,u,des->xev,d_xi(des,i),NULL,(INT)0);
+    }
+    /* designmatrix(lf,des); */
+    
+    /* find gamma (i.e. solve eqn 13.17 from book), using the secant method.
+     * As a side effect, this finds the other minimax coefficients.
+     * First, set some global pointers a, mm_lf, mm_des.
+     * Note that 13.17 is rewritten as
+     *   g2 = sum |l_i(x)| (||xi-x||^(p+1) M/(s*(p+1)!))
+     * where g2 = gamma * s * (p+1)! / M. The gam variable below is g2.
+     * The smoothing parameter is lf->dp[DALP] == M/s.
+     */
+    mm_lf = lf;
+    mm_des = des;
+    gam = solve_secant(findab, 0.0, 0.0,1.0, 0.0000001, BDF_EXPRIGHT, &err_flag);
+    
+    /*
+     * Set the smoothing weights, in preparation for the actual fit.
+     */
+    h = 0.0; m = 0;
+    for (i=0; i<lf->mi[MN]; i++)
+    { des->w[m] = weightmm(des->cf, des->wd[i],&des->X[i*lf->mi[MP]],lf->mi,gam);
+        if (des->w[m]>0)
+        { if (des->di[i]>h) h = des->di[i];
+            des->ind[m] = i;
+            m++;
+        }
+    }
+    des->n = m;
+    return(h);
+}
diff --git a/src/locfit/mutil.h b/src/locfit/mutil.h
new file mode 100644
index 0000000..3a7d825
--- /dev/null
+++ b/src/locfit/mutil.h
@@ -0,0 +1,56 @@
+/*
+ *   Copyright (c) 1998-2000 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *   Headers for math utility functions.
+ */
+
+#ifndef I_MUT_H
+#define I_MUT_H
+
+typedef struct {
+  double *Z;   /* jacobian matrix, length p*p          */
+  double *Q;   /* eigenvalue matrix, length p*p        */
+  double *wk;  /* work vector in eig_solve, length p   */
+  double *dg;  /* diag vector in eigd, length p        */
+  int p;       /* dimension */
+  int st;      /* status    */
+  int sm;      /* requested decomposition */
+} jacobian;
+
+/* m_jacob.c */
+extern int jac_reqd();
+extern double *jac_alloc();
+extern void jacob_dec(),   chol_dec(),   eig_dec();
+extern int  jacob_solve(), chol_solve(), eig_solve();
+extern int  jacob_hsolve(),chol_hsolve(),eig_hsolve();
+extern double jacob_qf(),  chol_qf(),    eig_qf();
+
+/* m_max.c */
+extern double max_grid(), max_golden(), max_quad(), max_nr();
+
+/* solve.c */
+extern double solve_secant(), solve_nr(), solve_fp();
+
+#define BDF_NONE  0
+#define BDF_EXPLEFT  1
+#define BDF_EXPRIGHT 2
+
+/* return codes for functions optimized by max_nr */
+#define NR_OK 0
+#define NR_INVALID 1
+#define NR_BREAK   2
+#define NR_REDUCE  3
+#define NR_NCON  10
+#define NR_NDIV  11
+
+
+/* jacobian status definitions */
+#define JAC_RAW 0
+#define JAC_CHOL 1
+#define JAC_EIG  2
+#define JAC_EIGD 3
+
+
+#endif  /* define I_MUT_H */
diff --git a/src/locfit/nbhd.c b/src/locfit/nbhd.c
new file mode 100644
index 0000000..23b8fe4
--- /dev/null
+++ b/src/locfit/nbhd.c
@@ -0,0 +1,226 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *
+ *  Functions for determining bandwidth; smoothing neighborhood
+ *  and smoothing weights.
+ */
+
+#include "local.h"
+
+extern vari *vb;
+
+double rho(x,sc,d,kt,sty) /* ||x|| for appropriate distance metric */
+double *x, *sc;
+INT d, kt, *sty;
+{ double rhoi[MXDIM], s;
+  INT i;
+  for (i=0; i<d; i++)
+  { if (sty!=NULL)
+    { switch(sty[i])
+      { case STANGL:  rhoi[i] = 2*sin(x[i]/(2*sc[i])); break;
+        case STCPAR: rhoi[i] = 0; break;
+        default: rhoi[i] = x[i]/sc[i];
+    } }
+    else rhoi[i] = x[i]/sc[i];
+  }
+
+  if (d==1) return(fabs(rhoi[0]));
+
+  s = 0;
+  if (kt==KPROD)
+  { for (i=0; i<d; i++)
+    { rhoi[i] = fabs(rhoi[i]);
+      if (rhoi[i]>s) s = rhoi[i];
+    }
+    return(s);
+  }
+
+  if (kt==KSPH)
+  { for (i=0; i<d; i++)
+      s += rhoi[i]*rhoi[i];
+    return(sqrt(s));
+  }
+
+  ERROR(("rho: invalid kt"));
+  return(0.0);
+}
+
+double kordstat(x,k,n,ind)
+double *x;
+INT k, n, *ind;
+{ INT i, i0, i1, l, r;
+  double piv;
+  if (k<1) return(0.0);
+  i0 = 0; i1 = n-1;
+  while (1)
+  { piv = x[ind[(i0+i1)/2]];
+    l = i0; r = i1;
+    while (l<=r)
+    { while ((l<=i1) && (x[ind[l]]<=piv)) l++;
+      while ((r>=i0) && (x[ind[r]]>piv)) r--;
+      if (l<=r) ISWAP(ind[l],ind[r]);
+    } /* now, x[ind[i0..r]] <= piv < x[ind[l..i1]] */
+    if (r<k-1) i0 = l;  /* go right */
+    else /* put pivots in middle */
+    { for (i=i0; i<=r; )
+        if (x[ind[i]]==piv) { ISWAP(ind[i],ind[r]); r--; }
+        else i++;
+      if (r<k-1) return(piv);
+      i1 = r;
+    }
+  }
+}
+
+/* check if i'th data point is in limits */
+INT inlim(lf,xlim,i,d)
+lfit *lf;
+double *xlim;
+INT i, d;
+{ INT j, k;
+  k = 1;
+  for (j=0; j<d; j++)
+  { if (xlim[j]<xlim[j+d])
+      k &= ((datum(lf,j,i)>=xlim[j]) & (datum(lf,j,i)<=xlim[j+d]));
+  }
+  return(k);
+}
+
+double compbandwid(di,ind,x,n,d,nn,fxh)
+double *di, *x, fxh;
+INT n, d, *ind, nn;
+{ INT i;
+  double nnh;
+
+#ifdef CVERSION
+  if (nn<0)
+    return(dareval(vb,0,x));
+#endif
+
+  if (nn<=0) return(fxh);
+
+  if (nn<n)
+    nnh = kordstat(di,nn,n,ind);
+  else
+  { nnh = 0;
+    for (i=0; i<n; i++) nnh = MAX(nnh,di[i]);
+    nnh = nnh*exp(log(1.0*nn/n)/d);
+  }
+  return(MAX(fxh,nnh));
+}
+
+/*
+  fast version of nbhd for ordered 1-d data
+*/
+double nbhd1(lf,des,k,fxh)
+lfit *lf;
+design *des;
+INT k;
+double fxh;
+{ double x, h, *xd;
+  INT i, l, r, m, n, z;
+  n = lf->mi[MN];
+  x = des->xev[0];
+  xd = dvari(lf,0);
+
+  /* find closest data point to x */
+  if (x<=xd[0]) z = 0;
+  else
+  if (x>=xd[n-1]) z = n-1;
+  else
+  { l = 0; r = n-1;
+    while (r-l>1)
+    { z = (r+l)/2;
+      if (xd[z]>x) r = z;
+              else l = z;
+    }
+    /* now, xd[0..l] <= x < x[r..n-1] */
+    if ((x-xd[l])>(xd[r]-x)) z = r; else z = l;
+  }
+  /* closest point to x is xd[z] */
+
+  if (k>0) /* set h to nearest neighbor bandwidth */
+  { l = r = z;
+    if (l==0) r = k-1;
+    if (r==n-1) l = n-k;
+    while (r-l<k-1)
+    { if ((x-xd[l-1])<(xd[r+1]-x)) l--; else r++;
+      if (l==0) r = k-1;
+      if (r==n-1) l = n-k;
+    }
+    h = x-xd[l];
+    if (h<xd[r]-x) h = xd[r]-x;
+  } else h = 0;
+
+  if (h<fxh) h = fxh;
+
+  m = 0;
+  if (xd[z]>x) z--; /* so xd[z]<=x */
+  /* look left */
+  for (i=z; i>=0; i--) if (inlim(lf,lf->xl,i,1))
+  { des->di[i] = x-xd[i];
+    des->w[m] = weight(lf,&xd[i],&x,h,1,des->di[i]);
+    if (des->w[m]>0)
+    { des->ind[m] = i;
+      m++; 
+    } else i = 0;
+  }
+  /* look right */
+  for (i=z+1; i<n; i++) if (inlim(lf,lf->xl,i,1))
+  { des->di[i] = xd[i]-x;
+    des->w[m] = weight(lf,&xd[i],&x,h,1,des->di[i]);
+    if (des->w[m]>0)
+    { des->ind[m] = i;
+      m++; 
+    } else i = n;
+  }
+
+  des->n = m;
+  return(h);
+}
+
+double nbhd(lf,des,nn,fxh,redo)
+lfit *lf;
+design *des;
+double fxh;
+INT redo, nn;
+{ INT d, i, j, m, n, *mi;
+  double h, u[MXDIM];
+
+  mi = lf->mi;
+
+  if (mi[MKT]==KCE) return(0.0);
+  d = mi[MDIM]; n = mi[MN];
+
+  /* ordered 1-dim; use fast searches */
+  if ((nn<=n) & (lf->ord) & (mi[MKER]!=WMINM) & (lf->sty[0]!=STANGL))
+    return(nbhd1(lf,des,nn,fxh));
+
+  if (!redo)
+  { for (i=0; i<n; i++)
+    { for (j=0; j<d; j++) u[j] = datum(lf,j,i)-des->xev[j];
+      des->di[i] = rho(u,lf->sca,d,mi[MKT],lf->sty);
+      des->ind[i] = i;
+    }
+  }
+  else
+    for (i=0; i<n; i++) des->ind[i] = i;
+
+  if (mi[MKER]==WMINM) return(minmax(lf,des));
+
+  h = compbandwid(des->di,des->ind,des->xev,n,mi[MDIM],nn,fxh);
+
+  m = 0;
+  for (i=0; i<n; i++) if (inlim(lf,lf->xl,i,d))
+  { for (j=0; j<d; j++) u[j] = datum(lf,j,i);
+    des->w[m] = weight(lf,u,des->xev,h,1,des->di[i]);
+    if (des->w[m]>0)
+    { des->ind[m] = i;
+      m++;
+    }
+  }
+  des->n = m;
+  return(h);
+}
diff --git a/src/locfit/pcomp.c b/src/locfit/pcomp.c
new file mode 100644
index 0000000..ad2a749
--- /dev/null
+++ b/src/locfit/pcomp.c
@@ -0,0 +1,191 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ * 
+ *
+ * functions for computing and subtracting, adding the
+ * parametric component
+ */
+
+#include "local.h"
+
+INT noparcomp(lf)
+lfit *lf;
+{ 
+    INT tg;
+    if (lf->mi[MDEG0]<lf->mi[MDEG]) return(1);
+    if (lf->mi[MUBAS]) return(1);
+    tg = lf->mi[MTG] & 63;
+    if (tg<=THAZ) return(1);
+    if (tg==TROBT) return(1);
+    if (tg==TCAUC) return(1);
+    return(0);
+}
+
+INT hasparcomp(lf)
+lfit *lf;
+{ 
+    return(lf->mi[MPC]);
+}
+
+int pc_reqd(d,p)
+INT d, p;
+{ 
+    return(d + 2*p + jac_reqd(p));
+}
+
+void pcchk(pc,d,p,lc)
+paramcomp *pc;
+INT d, p, lc;
+{ 
+    //INT k;
+    double *z;
+    pc->wk = checkvarlen(pc->wk,pc_reqd(d,p),"_pcwork",VDOUBLE);
+    z = vdptr(pc->wk);
+    //k = 0;
+    
+    pc->xbar = z; z += d;
+    pc->coef = z; z += p;
+    pc->f    = z; z += p;
+    
+    z = jac_alloc(&pc->xtwx,p,z);
+    pc->xtwx.p = p;
+}
+
+void compparcomp(des,lf,nopc)
+design *des;
+lfit *lf;
+INT nopc;
+{ 
+    INT i, j, k;
+    double wt, sw;
+    paramcomp *pc;
+    pc = &lf->pc;
+    pcchk(pc,lf->mi[MDIM],lf->mi[MP],1);
+    
+    if (pc == NULL)
+    {
+        fprintf(stderr, "Error: locfit cannot allocate pc working memory\n");
+        pcchk(pc,lf->mi[MDIM],lf->mi[MP],1);
+        return;
+    }
+    
+    for (i=0; i<lf->mi[MDIM]; i++) pc->xbar[i] = 0.0;
+    sw = 0.0;
+    for (i=0; i<lf->mi[MN]; i++)
+    { 
+        wt = prwt(lf,i);
+        sw += wt;
+        for (j=0; j<lf->mi[MDIM]; j++)
+            pc->xbar[j] += datum(lf,j,i)*wt;
+        des->ind[i] = i;
+        des->w[i] = 1.0;
+    }
+    for (i=0; i<lf->mi[MDIM]; i++) pc->xbar[i] /= sw;
+    if ((nopc) || noparcomp(lf))
+    { lf->mi[MPC] = 0;
+        return;
+    }
+    lf->mi[MPC] = 1;
+    des->xev = pc->xbar;
+    k = locfit(lf,des,0.0,0);
+
+    if (lf_error) return;
+    switch(k)
+    { case LF_NOPT:
+            ERROR(("compparcomp: no points in dataset?"));
+            return;
+        case LF_INFA:
+            ERROR(("compparcomp: infinite parameters in param. component"));
+            return;
+        case LF_NCON:
+            ERROR(("compparcom: not converged"));
+            return;
+        case LF_OOB:
+            ERROR(("compparcomp: parameters out of bounds"));
+            return;
+        case LF_PF:
+            WARN(("compparcomp: perfect fit"));
+        case LF_OK:
+            for (i=0; i<lf->mi[MP]; i++)
+            { pc->coef[i] = des->cf[i];
+                pc->xtwx.dg[i] = des->xtwx.dg[i];
+                pc->xtwx.wk[i] = des->xtwx.wk[i];
+            }
+            for (i=0; i<lf->mi[MP]*lf->mi[MP]; i++)
+            { pc->xtwx.Z[i] = des->xtwx.Z[i];
+                pc->xtwx.Q[i] = des->xtwx.Q[i];
+            }
+            pc->xtwx.sm = des->xtwx.sm;
+            pc->xtwx.st = des->xtwx.st;
+            return;
+        default:
+            ERROR(("compparcomp: locfit unknown return status %d",k));
+            return;
+    }
+}
+
+void subparcomp(des,lf,coef)
+design *des;
+lfit *lf;
+double *coef;
+{ INT i, *deriv, nd;
+    
+    if (!hasparcomp(lf)) return;
+    
+    deriv = lf->deriv;
+    nd = lf->nd;
+    fitfun(lf,des->xev,lf->pc.xbar,des->f1,deriv,nd);
+    coef[0] -= innerprod(lf->pc.coef,des->f1,lf->mi[MP]);
+    if (des->ncoef == 1) return;
+    
+    for (i=0; i<lf->mi[MDIM]; i++)
+    { deriv[nd] = i;
+        fitfun(lf,des->xev,lf->pc.xbar,des->f1,deriv,nd+1);
+        coef[i+1] -= innerprod(lf->pc.coef,des->f1,lf->mi[MP]);
+    }
+}
+
+void subparcomp2(des,lf,vr,il)
+design *des;
+lfit *lf;
+double *vr, *il;
+{ double t0, t1;
+    INT i, *deriv, nd, *mi;
+    
+    if (!hasparcomp(lf)) return;
+    
+    mi = lf->mi;
+    deriv = lf->deriv;
+    nd = lf->nd;
+    fitfun(lf,des->xev,lf->pc.xbar,des->f1,deriv,nd);
+    for (i=0; i<mi[MP]; i++) lf->pc.f[i] = des->f1[i];
+    jacob_solve(&lf->pc.xtwx,des->f1);
+    t0 = sqrt(innerprod(lf->pc.f,des->f1,mi[MP]));
+    vr[0] -= t0;
+    il[0] -= t0;
+    if ((t0==0) | (des->ncoef==1)) return;
+    
+    for (i=0; i<mi[MDIM]; i++)
+    { deriv[nd] = i;
+        fitfun(lf,des->xev,lf->pc.xbar,lf->pc.f,deriv,nd+1);
+        t1 = innerprod(lf->pc.f,des->f1,mi[MP])/t0;
+        vr[i+1] -= t1;
+        il[i+1] -= t1;
+    }
+}
+
+double addparcomp(lf,x,c)
+lfit *lf;
+double *x;
+int c;
+{ double y;
+    if (!hasparcomp(lf)) return(0.0);
+    fitfun(lf,x,lf->pc.xbar,lf->pc.f,lf->deriv,lf->nd);
+    if (c==PCOEF) return(innerprod(lf->pc.coef,lf->pc.f,lf->mi[MP]));
+    if ((c==PNLX)|(c==PT0)|(c==PVARI))
+    { y = sqrt(jacob_qf(&lf->pc.xtwx,lf->pc.f));
+        return(y);
+    }
+    return(0.0);
+}
diff --git a/src/locfit/pout.c b/src/locfit/pout.c
new file mode 100644
index 0000000..04eab50
--- /dev/null
+++ b/src/locfit/pout.c
@@ -0,0 +1,780 @@
+/*
+ *   Copyright (c) 1996-2000 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ * print a plot structure in the format of various graph packages
+ */
+
+#include "local.h"
+
+#ifdef CVERSION
+
+#define XCON(x) (INT)(i0+(i1-i0)*(x-px[0])/(px[1]-px[0]))
+#define YCON(y) (INT)(k0+(k1-k0)*(y-py[0])/(py[1]-py[0]))
+
+extern double sin(), cos(), sqrt(), atan2(), ceil(), floor(), log10(), pow();
+static INT i0, i1, k0, k1, cw, ch;
+#ifdef YAWN
+static FILE *plf;
+#endif
+extern INT curwin, lfcm[10];
+
+#ifdef NOPIPES
+FILE *popen(const char *cmd,const char *mode) { return(NULL); }
+int pclose(FILE *file) { return(0); }
+#endif
+
+extern device devps, devwin;
+
+char f2(fmt)
+char *fmt;
+{ char *z;
+  z = strchr(fmt,',');
+  if (z==NULL) return('d');
+  z++;
+  return(*z);
+}
+
+INT pretty(xl,k,z)
+double *xl, *z;
+INT k;
+{ double dlt, m;
+  INT i, j, u;
+  if (k<=0) return(0);
+  dlt = (xl[1]-xl[0])/k;
+  m = floor(log10(dlt));
+  dlt *= pow(10.0,-m);
+  if (dlt<2) u = 2;
+    else if (dlt<5) u = 5;
+      else { u = 1; m++; } /* increments should be u*10^m; */
+  dlt = u*pow(10.0,m);
+  i = (INT)ceil(xl[0]/dlt);
+  j = 0;
+  while ((j<k) && ((i+j)*dlt<=xl[1]))
+  { z[j] = (i+j)*dlt;
+    j++;
+  }
+  return(j);
+}
+
+void isgrid(xyz)
+plxyz *xyz;
+{ INT i, n;
+  vari *x, *y, *z;
+  x = xyz->x; y = xyz->y; z = xyz->z;
+  if ((x!=NULL) & (y!=NULL) & (z!=NULL))
+  { if ((x->n*y->n)==z->n)
+    { xyz->nx = x->n;
+      xyz->ny = y->n;
+      xyz->n = z->n;
+      xyz->t = 1;
+      return;
+    }
+    if ((x->n>1) & (y->n>1))
+    { i = 0; n = z->n;
+      while ((i<y->n) && (vitem(y,0)==vitem(y,i))) i++;
+      if ((i>1) && (i*(n/i)==n))
+      { xyz->nx = n/i;
+        xyz->ny = i;
+        xyz->n = n;
+        xyz->t = 1;
+        return;
+      }
+    }
+  }
+  xyz->t = 0;
+  xyz->n = 0;
+  if ((x!=NULL) && (x->n>xyz->n)) xyz->n = x->n;
+  if ((y!=NULL) && (y->n>xyz->n)) xyz->n = y->n;
+  if ((z!=NULL) && (z->n>xyz->n)) xyz->n = z->n;
+}
+
+void getxyzitem(x,xyz,i)
+plxyz *x;
+double xyz[3];
+int i;
+{ xyz[0] = vitem(x->x,i);
+  if (x->t==1)
+    xyz[1] = vitem(x->y,i/x->x->n);
+  else
+    xyz[1] = vitem(x->y,i);
+  xyz[2] = vitem(x->z,i);
+}
+
+static double xl[2], yl[2], zl[2], px[2], py[2];
+
+void project(z,x,theta,phi)
+double *z, *x, theta, phi;
+{ double z0, z1, z2;
+  z0 = (z[0]-xl[0])/(xl[1]-xl[0]);
+  z1 = (z[1]-yl[0])/(yl[1]-yl[0]);
+  z2 = (z[2]-zl[0])/(zl[1]-zl[0]);
+  x[0] = cos(theta)*z0-sin(theta)*z1;
+  x[1] = (sin(theta)*z0+cos(theta)*z1)*cos(phi)+sin(phi)*z2;
+}
+
+void iproject(z,i,theta,phi)
+double *z, theta, phi;
+INT *i;
+{ double x[2];
+  project(z,x,theta,phi);
+  i[0] = XCON(x[0]); i[1] = YCON(x[1]);
+}
+
+void line3d(z1,z2,theta,phi,dev,col)
+double *z1, *z2, theta, phi;
+device *dev;
+INT col;
+{ INT x1[2], x2[2];
+  iproject(z1,x1,theta,phi);
+  iproject(z2,x2,theta,phi);
+  dev->SetColor(lfcm[col]);
+  dev->DrawLine(x1[0],x1[1],x2[0],x2[1]);
+}
+
+void xyztext(tx,x,ah,av,theta,phi,dev,col)
+double *x, theta, phi;
+INT ah, av, col;
+char *tx;
+device *dev;
+{ INT xy[2];
+  iproject(x,xy,theta,phi);
+  dev->SetColor(lfcm[col]);
+  dev->DoText(0,xy[0],xy[1],tx,ah,av);
+}
+
+int getgreylevel(z)
+double z;
+{ int c;
+  c = 8+11*(z-zl[0])/(zl[1]-zl[0]);
+  if (c<8) return(8);
+  if (c>18)return(18);
+  return(c);
+}
+
+void points3d(x,theta,phi,dev,type)
+plxyz *x;
+double theta, phi;
+char type;
+device *dev;
+{ INT i, xy[2];
+  double xyz[3];
+  for (i=0; i<x->n; i++)
+  {
+    getxyzitem(x,xyz,i);
+    iproject(xyz,xy,theta,phi);
+    if (type=='q') 
+      dev->SetColor(getgreylevel(xyz[2]));
+    else
+      dev->SetColor(lfcm[CPOI]);
+    dev->DrawPoint(xy[0],xy[1],x->pch);
+  }
+}
+
+void lines3d(xyz,theta,phi,dev)
+plxyz *xyz;
+double theta, phi;
+device *dev;
+{ INT i;
+  double x0[3], x1[3];
+  getxyzitem(xyz,x0,0);
+  for (i=1; i<xyz->n; i++)
+  { if (i&1)
+    { getxyzitem(xyz,x1,i);
+      line3d(x0,x1,theta,phi,dev,CLIN);
+    }
+    else 
+    { getxyzitem(xyz,x0,i);
+      line3d(x1,x0,theta,phi,dev,CLIN);
+    }
+  }
+}
+
+void segments(xyz0,xyz1,theta,phi,dev)
+plxyz *xyz0, *xyz1;
+double theta, phi;
+device *dev;
+{ INT i, n;
+  double x0[3], x1[3];
+  n = xyz0->n;
+  if (xyz1->n>n) n = xyz1->n;
+  for (i=0; i<n; i++)
+  { getxyzitem(xyz0,x0,i);
+    getxyzitem(xyz1,x1,i);
+    line3d(x0,x1,theta,phi,dev,CSEG);
+  }
+}
+
+double spl(z0,z1)
+double z0, z1;
+{ if (z0-z1==0.0) return(0.5);
+  return(z0/(z0-z1));
+}
+
+void contour3d(x,theta,phi,dev,sl,nsl)
+plxyz *x;
+double theta, phi, *sl;
+INT nsl;
+device *dev;
+{ INT i, j, k, nx, ny, s;
+  double xyz[4][3], u[4], x0[3], x1[3], x2[3], x3[3], r;
+  char lb[20];
+  if (x->t==0) ERROR(("Contour: not a grid"));
+  if (lf_error) return;
+  if (nsl==0) nsl = pretty(zl,10,sl);
+  nx = x->nx; ny = x->ny;
+  for (k=0; k<nsl; k++)
+  { x0[2] = x1[2] = x2[2] = x3[2] = sl[k];
+    for (i=0; i<nx-1; i++)
+      for (j=0; j<ny-1; j++)
+      { sprintf(lb,"%g",sl[k]);
+        getxyzitem(x,xyz[0],i+j*nx);
+        getxyzitem(x,xyz[1],(i+1)+j*nx);
+        getxyzitem(x,xyz[2],i+(j+1)*nx);
+        getxyzitem(x,xyz[3],i+1+(j+1)*nx);
+        u[0] = xyz[0][2]-sl[k];
+        u[1] = xyz[1][2]-sl[k];
+        u[2] = xyz[2][2]-sl[k];
+        u[3] = xyz[3][2]-sl[k];
+        if (u[0]*u[1]<=0) /* bottom of cell */
+        { r = spl(u[0],u[1]);
+          x0[0] = (1-r)*xyz[0][0]+r*xyz[1][0];
+          x0[1] = xyz[0][1];
+          if (j==0) xyztext(lb,x0,-1,-1,theta,phi,dev,CCLA);
+        }
+        if (u[1]*u[3]<=0) /* right of cell */
+        { r = spl(u[1],u[3]);
+          x1[0] = xyz[1][0];
+          x1[1] = (1-r)*xyz[1][1]+r*xyz[3][1];
+          if (i==nx-2) xyztext(lb,x1,-1,0,theta,phi,dev,CCLA);
+        }
+        if (u[2]*u[3]<=0) /* top of cell */
+        { r = spl(u[2],u[3]);
+          x2[0] = (1-r)*xyz[2][0]+r*xyz[3][0];
+          x2[1] = xyz[2][1];
+          if (j==ny-2) xyztext(lb,x2,-1,1,theta,phi,dev,CCLA);
+        }
+        if (u[0]*u[2]<=0) /* left of cell */
+        { r = spl(u[0],u[2]);
+          x3[0] = xyz[0][0];
+          x3[1] = (1-r)*xyz[0][1]+r*xyz[2][1];
+          if (i==0) xyztext(lb,x3,-1,0,theta,phi,dev,CCLA);
+        }
+
+        s = 8*(u[3]>0)+4*(u[2]>0)+2*(u[1]>0)+(u[0]>0);
+        switch(s)
+        { case 0:
+          case 15: break;
+          case 1:
+          case 14:
+            line3d(x0,x3,theta,phi,dev,CCON);
+            break;
+          case 2:
+          case 13:
+            line3d(x0,x1,theta,phi,dev,CCON);
+            break;
+          case 3:
+          case 12:
+            line3d(x3,x1,theta,phi,dev,CCON);
+            break;
+          case 4:
+          case 11:
+            line3d(x3,x2,theta,phi,dev,CCON);
+            break;
+          case 5:
+          case 10:
+            line3d(x0,x2,theta,phi,dev,CCON);
+            break;
+          case 6:
+          case 9:
+            line3d(x0,x1,theta,phi,dev,CCON);
+            break;
+          case 7:
+          case 8:
+            line3d(x1,x2,theta,phi,dev,CCON);
+            break;
+          default: ERROR(("severe contouring error..."));
+        }
+      }
+  }
+}
+
+double angle(x0,x1,x2) /* rotation angle from (x0,x1) to (x0,x2) */
+double *x0, *x1, *x2;
+/* If x0=0, ||x1=1|| then express
+   x2 = u x1 + v y1       where y1=(-x11,x10) = 90 deg anticlk rot.
+      i.e. u = <x1,x2>  v = <y1,x2>
+   tan(theta) = v/u
+   atan2(v,u) returns positive for anticlkws rot;
+                      negative for clockwise rot.
+*/
+{ double u, v;
+  u =  (x1[0]-x0[0])*(x2[0]-x0[0]) + (x1[1]-x0[1])*(x2[1]-x0[1]);
+  v = -(x1[1]-x0[1])*(x2[0]-x0[0]) + (x1[0]-x0[0])*(x2[1]-x0[1]);
+  return(atan2(v,u));
+}
+
+void persp3d(xyz,theta,phi,DP,sl,nsl)
+plxyz *xyz;
+double theta, phi, *sl;
+void (*DP)();
+INT nsl;
+{ INT i, j, k, m, nx, ny, ii, jj, cb, cp, cx[4], cy[4], xhi, yhi;
+  double u[4][3], w[4][2], r;
+  if (xyz->t==0) ERROR(("persp3d: not a grid"));
+  if (lf_error) return;
+  /* theta=135 --- 225
+            |       |
+            45 --- 315;
+     i.e start at top right for theta=45 e.t.c. Reverse if sin(phi)<0.
+     x starts hi if cos(theta)>0
+     y starts hi if sin(theta)>0
+  */
+  xhi = (cos(theta)*sin(phi)) > 0;
+  yhi = (sin(theta)*sin(phi)) > 0;
+  nx = xyz->nx; ny = xyz->ny;
+  for (i=0; i<nx-1; i++)
+    for (j=0; j<ny-1; j++)
+    { for (k=0; k<4; k++)
+      {  /*     1 -- 2
+                |    |
+                0 -- 3   */
+        ii = (xhi) ? nx-2-i : i;
+        jj = (yhi) ? ny-2-j : j;
+        ii += (k>1);
+        jj += (k==1)+(k==2);
+        m = jj*nx+ii;
+        getxyzitem(xyz,u[k],m);
+        project(u[k],w[k],theta,phi);
+        cx[k] = XCON(w[k][0]); cy[k] = YCON(w[k][1]);
+      }
+
+      switch(xyz->type)
+      { case 'w':
+          /* wireframe:
+             from top, use color CPA1 for border, CPA2 for patch
+             angles are anti-clock from top; r approx 2pi
+                             clock from bot; r approx -2pi
+          */
+          r = angle(w[0],w[3],w[1])+angle(w[1],w[0],w[2])
+             +angle(w[2],w[1],w[3])+angle(w[3],w[2],w[0]);
+          if (r>0) { cb = lfcm[CPA1]; cp = lfcm[CPA2]; }
+              else { cp = lfcm[CPA1]; cb = lfcm[CPA2]; }
+          DP(cx,cy,cp,cb);
+          break;
+        case 'i': /* image */
+          if (nsl==0) nsl = pretty(zl,10,sl);
+          r = u[0][2] + u[1][2] + u[2][2] + u[3][2];
+          cb = cp = getgreylevel(r/4);
+          DP(cx,cy,cp,cb);
+          break;
+      }
+    }
+}
+
+void updatelim(v,xl)
+vari *v;
+double *xl;
+{ INT i;
+  if ((v==NULL) || (vlength(v)==0)) return;
+  if (xl[0]==xl[1])
+    xl[0] = xl[1] = vitem(v,0);
+  for (i=0; i<vlength(v); i++)
+  { if (vitem(v,i)<xl[0]) xl[0] = vitem(v,i);
+    if (vitem(v,i)>xl[1]) xl[1] = vitem(v,i);
+  }
+}
+
+void axis(z1,z2,zl,lab,theta,phi,a,s,dev)
+double *z1, *z2, *zl, theta, phi;
+char *lab;
+INT a, s;
+device *dev;
+{ double x1[3], x2[3], z[50];
+  INT i, u0, u1, v0, v1, n, horiz;
+  char lb[20];
+  dev->SetColor(lfcm[CAXI]);
+  project(z1,x1,theta,phi);
+  project(z2,x2,theta,phi);
+  u0 = XCON(x1[0]); v0 = XCON(x2[0]);
+  u1 = YCON(x1[1]); v1 = YCON(x2[1]);
+  horiz = abs(v0-u0)>abs(v1-u1);
+  dev->DrawLine(u0,u1,v0,v1);
+  n = (INT)sqrt((double)((v0-u0)*(v0-u0)+(v1-u1)*(v1-u1)))/((horiz) ? 5*cw : 2*ch);
+  if (n>50) n = 50;
+  n = pretty(zl,n,z);
+  if (n==0) return;
+  x1[0] = z1[0];  x1[1] = z1[1]; x1[2] = z1[2];
+  if (abs(v0-u0)>abs(v1-u1)) /* horizontal axis */
+  { dev->SetColor(lfcm[CTEX]);
+    if (lab!=NULL)
+      dev->DoText(0,(u0+v0)/2,(u1+v1)/2+s*(dev->ticklength+ch),lab,0,s);
+    for (i=0; i<n; i++)
+    { x1[a] = z[i];
+      project(x1,x2,theta,phi);
+      u0 = XCON(x2[0]); u1 = YCON(x2[1]);
+      v0 = u0; v1 = u1+s*dev->ticklength;
+      sprintf(lb,"%g",z[i]);
+      dev->SetColor(lfcm[CAXI]);
+      dev->DrawLine(u0,u1,v0,v1);
+      dev->SetColor(lfcm[CTEX]);
+      dev->DoText(0,v0,v1,lb,0,s);
+  } }
+  else /* vertical axis */
+  { s = 2*((2*v0)>(i0+i1))-1;
+    dev->SetColor(lfcm[CTEX]);
+    if (lab!=NULL)
+      dev->DoText(0,v0,v1-ch,lab,-s,-1);
+    for (i=0; i<n; i++)
+    { x1[a] = z[i];
+      project(x1,x2,theta,phi);
+      u0 = XCON(x2[0]); u1 = YCON(x2[1]);
+      v0 = u0+s*dev->ticklength; v1 = u1;
+      sprintf(lb,"%g",z[i]);
+      dev->SetColor(lfcm[CAXI]);
+      dev->DrawLine(u0,u1,v0,v1);
+      dev->SetColor(lfcm[CTEX]);
+      dev->DoText(0,v0,v1,lb,-s,0);
+  } }
+}
+
+void plotxwin(pl,dev,wn,w,h,rd)
+plots *pl;
+device *dev;
+INT wn, w, h, rd;
+{ INT i, j, k, s;
+  double z[3], z2[3], xx[2], vx, vy, vz;
+  static double theta, phi;
+  plxyz *xyz;
+  if (pl->ty==PLNONE) return;
+  if (h<=0) h = dev->defth;
+  if (w<=0) w = dev->deftw;
+  if (!dev->makewin(&w,&h,wn,rd)) return;
+  dev->TextDim(0,"0",&cw,&ch);
+  i0 = 4*cw+dev->ticklength;   i1 = w-2*cw;
+  k0 = h-3*ch-dev->ticklength; k1 = 3*ch;
+  dev->ClearScreen(lfcm[CBAK]);
+  if (pl->xl[0]<pl->xl[1])
+  { xl[0] = pl->xl[0]; xl[1] = pl->xl[1]; }
+  else
+  { xl[0] = xl[1] = 0.0;
+    for (i=0; i<pl->xyzs->n; i++)
+    { xyz = (plxyz *)viptr(pl->xyzs,i);
+      updatelim(xyz->x,xl);
+    }
+    if (xl[0]==xl[1]) { xl[0] -= 0.5; xl[1] += 0.5; }
+  }
+  if (pl->yl[0]<pl->yl[1])
+  { yl[0] = pl->yl[0]; yl[1] = pl->yl[1]; }
+  else
+  { yl[0] = yl[1] = 0.0;
+    for (i=0; i<pl->xyzs->n; i++)
+    { xyz = (plxyz *)viptr(pl->xyzs,i);
+      updatelim(xyz->y,yl);
+    }
+    if (yl[0]==yl[1]) { yl[0] -= 0.5; yl[1] += 0.5; }
+  }
+  if (pl->zl[0]<pl->zl[1])
+  { zl[0] = pl->zl[0]; zl[1] = pl->zl[1]; }
+  else
+  { zl[0] = zl[1] = 0.0;
+    for (i=0; i<pl->xyzs->n; i++)
+    { xyz = (plxyz *)viptr(pl->xyzs,i);
+      updatelim(xyz->z,zl);
+    }
+    if (zl[0]==zl[1]) { zl[0] -= 0.5; zl[1] += 0.5; }
+  }
+  theta = pl->theta*PI/180; phi = pl->phi*PI/180;
+  vx = -sin(theta)*sin(phi);
+  vy = -cos(theta)*sin(phi);
+  vz = cos(phi);
+
+  for (i=0; i<2; i++)
+    for (j=0; j<2; j++)
+      for (k=0; k<2; k++)
+      { z[0] = xl[i]; z[1] = yl[j]; z[2] = zl[k];
+        project(z,xx,theta,phi);
+        if ((i+j+k==0) | (xx[0]<px[0])) px[0]=xx[0];
+        if ((i+j+k==0) | (xx[0]>px[1])) px[1]=xx[0];
+        if ((i+j+k==0) | (xx[1]<py[0])) py[0]=xx[1];
+        if ((i+j+k==0) | (xx[1]>py[1])) py[1]=xx[1];
+      }
+  s = 1-2*((cos(phi)<0)^(sin(phi)<0));
+  z[0] = xl[0]; z2[0] = xl[1];
+  z[1] = z2[1] = yl[(cos(theta)<0)^(sin(phi)<0)];
+  z[2] = z2[2] = zl[cos(phi)<0];
+  axis(z,z2,xl,pl->xlab,theta,phi,0,s,dev);
+  z[0] = z2[0] = xl[(sin(theta)<0)^(sin(phi)<0)];
+  z[1] = yl[0]; z2[1] = yl[1];
+  z[2] = z2[2] = zl[cos(phi)<0];
+  axis(z,z2,yl,pl->ylab,theta,phi,1,s,dev);
+  z[0] = z2[0] = xl[cos(theta)<0];
+  z[1] = z2[1] = yl[sin(theta)>0];
+  z[2] = zl[0]; z2[2] = zl[1];
+  axis(z,z2,zl,pl->zlab,theta,phi,2,s,dev);
+  if (strlen(pl->main)>0) dev->DoText(1,(i0+i1)/2,2*ch,pl->main,0,-1);
+ 
+  for (i=0; i<pl->xyzs->n; i++)
+  { xyz = viptr(pl->xyzs,i);
+    isgrid(xyz);
+    switch (xyz->type)
+    { case 'c':
+        contour3d(xyz,theta,phi,dev,pl->sl,pl->nsl);
+        break;
+      case 'i':
+        persp3d(xyz,theta,phi,dev->DrawPatch,pl->sl,pl->nsl);
+        break;
+      case 'b':
+        points3d(xyz,theta,phi,dev,'p');
+      case 'l':
+        lines3d(xyz,theta,phi,dev);
+        break;
+      case 'p':
+        points3d(xyz,theta,phi,dev,'p');
+        break;
+      case 'q':
+        points3d(xyz,theta,phi,dev,'q');
+        break;
+      case 's':
+        if (i==0) { ERROR(("invalid segements")); }
+        else
+        segments(viptr(pl->xyzs,i-1),xyz,theta,phi,dev);
+        break;
+      case 'w':
+        persp3d(xyz,theta,phi,dev->DrawPatch,pl->sl,pl->nsl);
+        break;
+  } }
+
+  dev->wrapup(rd);
+}
+
+#ifdef YAWN
+void plotmaple(pl)
+plots *pl;
+{ INT i, j;
+  plf = fopen("lfplot","w");
+  switch(pl->d)
+  { case 1:
+      fprintf(plf,"PLOT(\n");
+      for (j=0; j<pl->r; j++)
+      { fprintf(plf,"CURVES([\n");
+        for (i=0; i<pl->mx[j]; i++)
+        { if (i>0) fprintf(plf,",\n");
+          fprintf(plf,"[%f,%f]",pl->x[j][i],pl->y[j][i]);
+        }
+        fprintf(plf,"],\nCOLOUR(RGB,0,0,0)),\n");
+      }
+      if (pl->type[0]=='p') fprintf(plf,"STYLE(POINT),");
+      if (pl->main!=NULL) fprintf(plf,"TITLE(%s),",pl->main);
+      fprintf(plf,"AXESLABELS(%s,%s)",pl->xlab,pl->ylab);
+      fprintf(plf,");\n");
+      
+      break;
+    case 2:
+      fprintf(plf,"PLOT3D(GRID(%f..%f,%f..%f,[[\n",pl->x[0][0],pl->x[0][pl->mx[0]-1],pl->y[0][0],pl->y[0][pl->my[0]-1]);
+      for (i=0; i<pl->mx[0]; i++)
+      { if (i>0) fprintf(plf,"],\n[");
+        for (j=0; j<pl->my[0]; j++)
+        { if (j>0) fprintf(plf,",\n");
+          fprintf(plf,"%f",pl->z[0][i*pl->my[0]+j]);
+        }
+      }
+      fprintf(plf,"]]),\nAXESLABELS(%s,%s,%s),AXESSTYLE(FRAME)",pl->xlab,pl->ylab,pl->zlab);
+      if (pl->type[0]=='c') fprintf(plf,",STYLE(CONTOUR),CONTOURS(DEFAULT),ORIENTATION(-90,0.1),COLOUR(ZSHADING)");
+      if (pl->main!=NULL) fprintf(plf,",\nTITLE(%s)\n",pl->main);
+      fprintf(plf,");\n");
+      break;
+  }
+  fclose(plf);
+  printf("Created lfplot file; Maple format.\n");
+}
+
+void plotmathe(pl,fmt)
+plots *pl;
+char *fmt;
+{ INT i, j, aut;
+  static FILE *plm=NULL;
+  aut = f2(fmt)!='m';
+#ifdef NOPIPES
+  aut = 0;
+#endif
+  if (aut)
+  { if (plm==NULL) plm = (FILE *)popen("math >/dev/null","w");
+    plf = plm;
+  }
+  else
+    plf = fopen("lfplot","w");
+  switch(pl->d)
+  { case 1:
+      fprintf(plf,"ListPlot[{{\n");
+      for (i=0; i<pl->mx[0]; i++)
+      { if (i>0) fprintf(plf,"},\n{");
+        fprintf(plf,"%f,%f",pl->x[0][i],pl->y[0][i]);
+      }
+      fprintf(plf,"}}");
+      fprintf(plf,",AxesLabel->{%s,%s}",pl->xlab,pl->ylab);
+      if (pl->type[0]=='l') fprintf(plf,",PlotJoined->True");
+      break;
+    case 2:
+      if (pl->type[0]=='c') fprintf(plf,"ListContourPlot[{{");
+                    else fprintf(plf,"ListPlot3D[{{");
+      for (j=0; j<pl->my[0]; j++)
+      { if (j>0) fprintf(plf,"},\n{");
+        for (i=0; i<pl->mx[0]; i++)
+        { if (i>0) fprintf(plf,",\n");
+          fprintf(plf,"%f",pl->z[0][i*pl->my[0]+j]);
+        }
+      }
+      fprintf(plf,"}},\nMeshRange->{{");
+      fprintf(plf,"%f,%f},{%f,%f}}\n",pl->x[0][0],pl->x[0][pl->mx[0]-1],pl->y[0][0],pl->y[0][pl->my[0]-1]);
+      if (pl->type[0]=='c') fprintf(plf,",FrameLabel->{%s,%s}",pl->xlab,pl->ylab);
+                    else fprintf(plf,",AxesLabel->{%s,%s,%s}",pl->xlab,pl->ylab,pl->zlab);
+      break;
+  }
+  if (pl->main!=NULL) fprintf(plf,",PlotLabel->%s\n",pl->main);
+  fprintf(plf,"];\n");
+  if (aut)
+    fflush(plf);
+  else
+  { fclose(plf);
+    printf("Created lfplot file; Mathematica format.\n");
+  }
+}
+
+void plotmatlb(pl) /* Matlab */
+plots *pl;
+{ INT i, j;
+  plf = fopen("lfplot.m","w");
+  switch(pl->d)
+  { case 1:
+      fprintf(plf,"plot([");
+      for (i=0; i<pl->mx[0]; i++)
+      { if (i>0) putc(',',plf);
+        fprintf(plf,"%f",pl->x[0][i]);
+      } 
+      fprintf(plf,"],[");
+      for (i=0; i<pl->mx[0]; i++)
+      { if (i>0) putc(',',plf);
+        fprintf(plf,"%f",pl->y[0][i]);
+      }
+      fprintf(plf,"])\n");
+      break;
+    case 2:
+      if (pl->type[0]=='c') fprintf(plf,"contour([");
+                    else fprintf(plf,"mesh([");
+      for (i=0; i<pl->my[0]; i++) fprintf(plf,"%f ",pl->y[0][i]); 
+      fprintf(plf,"],[");
+      for (i=0; i<pl->mx[0]; i++) fprintf(plf,"%f ",pl->x[0][i]); 
+      fprintf(plf,"],[\n");
+      for (j=0; j<pl->my[0]; j++)
+      { fprintf(plf,"[");
+        for (i=0; i<pl->mx[0]; i++)
+          fprintf(plf,"%f ",pl->z[0][i*pl->my[0]+j]);
+        fprintf(plf,"]\n");
+      }
+      fprintf(plf,"])\n");
+      fprintf(plf,"xlabel('%s')\n",pl->xlab);
+      fprintf(plf,"ylabel('%s')\n",pl->ylab);
+      break;
+    default: ERROR(("plotmatlb: invalid dimension %d",pl->d));
+  }
+  if (pl->main!=NULL) fprintf(plf,"title('%s')\n",pl->main);
+  fclose(plf);
+  printf("Created lfplot.m file; matlab format.\n");
+}
+
+void plotgnup(pl,fmt)
+plots *pl;
+char *fmt;
+{ INT i, j, z, aut;
+  char m;
+  static FILE *plc=NULL;
+
+  /* first, the data file */
+  plf=fopen("lfplot.dat","w");
+  switch(pl->d)
+  { case 1:
+      z = pl->mx[0];
+      for (j=0; j<pl->r; j++) if (pl->mx[j]>z) z = pl->mx[j];
+      for (i=0; i<z; i++)
+      { for (j=0; j<pl->r; j++)
+          if (i<pl->mx[j])
+            fprintf(plf,"%f %f ",pl->x[j][i],pl->y[j][i]);
+          else
+            fprintf(plf,"%f %f ",pl->x[j][pl->mx[j]-1],pl->y[j][pl->mx[j]-1]);
+        fprintf(plf,"\n");
+      }
+      break;
+    case 2:
+      for (j=0; j<pl->my[0]; j++)
+      { for (i=0; i<pl->mx[0]; i++)
+          fprintf(plf,"%f %f %f\n",pl->x[0][i],pl->y[0][j],pl->z[0][i*pl->my[0]+j]);
+        fprintf(plf,"\n");
+      }
+  }
+  fclose(plf);
+
+  /* second, the command file */
+  m = f2(fmt);
+  aut = (m!='m');
+#ifdef NOPIPES
+  aut = 0;
+#endif
+  if (aut)
+  { if ((m=='s') && (plc!=NULL)) pclose(plc);
+    if ((m=='s') || (plc==NULL))
+      plc = (FILE *)popen("gnuplot","w");
+    plf = plc;
+  }
+  else plf = fopen("lfplot","w");
+  switch(pl->d)
+  { case 1:
+      fprintf(plf,"set nokey\n");
+      fprintf(plf,"set xlab \"%s\"\n",pl->xlab);
+      fprintf(plf,"set ylab \"%s\"\n",pl->ylab);
+      if (pl->main != NULL)
+        fprintf(plf,"set title \"%s\"\n",pl->main);
+      fprintf(plf,"plot ");
+      for (i=0; i<pl->r; i++)
+      { if (i>0) fprintf(plf,", ");
+        fprintf(plf,"\"lfplot.dat\" using %d:%d ",2*i+1,2*i+2);
+        switch(pl->type[i])
+        { case 'l': fprintf(plf,"with lines"); break;
+          case 'p': fprintf(plf,"with points"); break;
+          case 'b': fprintf(plf,"with linespoints"); break;
+        }
+      }
+      fprintf(plf,"\n");
+      break;
+    case 2:
+      fprintf(plf,"set xlab \"%s\"\n",pl->xlab);
+      fprintf(plf,"set ylab \"%s\"\n",pl->ylab);
+      fprintf(plf,"set zlab \"%s\"\n",pl->zlab);
+      if (pl->type[0]=='c')
+      { fprintf(plf,"set contour\n");
+        fprintf(plf,"set nosurface\n");
+        fprintf(plf,"set key\n");
+      }
+      else
+      { fprintf(plf,"set nocontour\n");
+        fprintf(plf,"set surface\n");
+        fprintf(plf,"set nokey\n");
+      }
+      fprintf(plf,"set view %g,%g\n",pl->phi,pl->theta);
+      fprintf(plf,"set parametric\n");
+      if (pl->main != NULL)
+        fprintf(plf,"set title \"%s\"\n",pl->main);
+      fprintf(plf,"splot \"lfplot.dat\" with lines\n");
+      break;
+  }
+  if (aut)
+    fflush(plf);
+  else
+  { fclose(plf);
+    printf("Created lfplot, lfplot.dat files; gnuplot format.\n");
+  }
+}
+#endif
+
+#endif
diff --git a/src/locfit/preplot.c b/src/locfit/preplot.c
new file mode 100644
index 0000000..2e355c4
--- /dev/null
+++ b/src/locfit/preplot.c
@@ -0,0 +1,222 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include "local.h"
+
+/*
+ preplot():  interpolates the fit to a new set of points.
+ lf  -- the fit structure.
+ des -- design structure; really only needed for parfit.
+ x   -- the points to predict at.
+ f   -- vector to return the predictions.
+ se  -- vector to return std errors (NULL if not req'd)
+ band-- char for conf band type. ('n'=none, 'g'=global etc.)
+ n   -- no of predictions (or vector of margin lengths for grid)
+ where -- where to predict:
+ 1 = points in the array x.
+ 2 = grid defined by margins in x.
+ 3 = data points from lf (ignore x).
+ 4 = fit points from lf (ignore x).
+ what -- what to predict.
+ (PCOEF etc; see lfcons.h file)
+ 
+ cpreplot(): C version front end.
+ setpppoints(): (C version) used to set preplot points.
+ */
+
+static char cb;
+double *sef, *fit, sigmahat;
+
+void predptall(lf,des,x,what,ev,i)
+lfit *lf;
+design *des;
+double *x;
+INT what, ev, i;
+{ double lik, rdf;
+    fit[i] = dointpoint(lf,des,x,what,ev,i);
+    if (cb=='n') return;
+    sef[i] = dointpoint(lf,des,x,PNLX,ev,i);
+    if (cb=='g')
+    { sef[i] *= sigmahat;
+        return;
+    }
+    if (cb=='l')
+    { lik = dointpoint(lf,des,x,PLIK,ev,i);
+        rdf = dointpoint(lf,des,x,PRDF,ev,i);
+        sef[i] *= sqrt(-2*lik/rdf);
+        return;
+    }
+    if (cb=='p')
+    { sef[i] = sigmahat*sqrt(1+sef[i]*sef[i]);
+        return;
+    }
+}
+
+void prepvector(lf,des,x,n,what) /* interpolate a vector */
+lfit *lf;
+design *des;
+double **x;
+INT n, what;
+{ INT i, j;
+    double xx[MXDIM];
+    for (i=0; i<n; i++)
+    { for (j=0; j<lf->mi[MDIM]; j++) xx[j] = x[j][i];
+        predptall(lf,des,xx,what,lf->mi[MEV],i);
+        if (lf_error) return;
+    }
+}
+
+void prepfitp(lf,des,what)
+lfit *lf;
+design *des;
+INT what;
+{ 
+    INT i;
+    //d = lf->mi[MDIM];
+    for (i=0; i<lf->nv; i++)
+    { predptall(lf,des,evpt(lf,i),what,EFITP,i);
+        if (lf_error) return;
+    }
+}
+
+void prepgrid(lf,des,x,mg,n,what) /* interpolate a grid given margins */
+design *des;
+lfit *lf;
+double **x;
+INT *mg, n, what;
+{ INT i, ii, j, d;
+    double xv[MXDIM];
+    d = lf->mi[MDIM];
+    for (i=0; i<n; i++)
+    { ii = i;
+        for (j=0; j<d; j++)
+        { xv[j] = x[j][ii%mg[j]];
+            ii /= mg[j];
+        }
+        predptall(lf,des,xv,what,lf->mi[MEV],i);
+        if (lf_error) return;
+    }
+}
+
+void preplot(lf,des,x,f,se,band,mg,where,what)
+lfit *lf;
+design *des;
+double **x, *f, *se;
+INT *mg, where, what;
+char band;
+{ INT d = 0, i, n;
+    double *xx[MXDIM];
+    d = lf->mi[MDIM];
+    fit = f;
+    sef = se;
+    cb = band;
+    if (cb!='n') sigmahat = sqrt(lf->dp[DRV]);
+    
+    switch(where)
+    { case 1: /* vector */
+            n = mg[0];
+            prepvector(lf,des,x,n,what);
+            break;
+        case 2: /* grid */
+            n = 1;
+            for (i=0; i<d; i++) n *= mg[i];
+            prepgrid(lf,des,x,mg,n,what);
+            break;
+        case 3: /* data */
+            n = lf->mi[MN];
+            if ((lf->mi[MEV]==EDATA) | (lf->mi[MEV]==ECROS))
+                prepfitp(lf,des,what);
+            else
+            { for (i=0; i<d; i++) xx[i] = dvari(lf,i);
+                prepvector(lf,des,xx,n,what);
+            }
+            break;
+        case 4: /* fit points */
+            n = lf->nv;
+            prepfitp(lf,des,what);
+            break;
+        default:
+            ERROR(("unknown where in preplot"));
+            return;
+    }
+    
+    if ((what==PT0)|(what==PVARI))
+        for (i=0; i<n; i++) f[i] = f[i]*f[i];
+}
+
+#ifdef CVERSION
+extern lfit lf;
+extern design des;
+
+void cpreplot(pp,vc,band)
+pplot *pp;
+vari *vc;
+char band;
+{ double *data[MXDIM];
+    INT j, mg[MXDIM];
+    for (j=0; j<pp->d; j++)
+    { data[j] = vdptr(pp->data[j]);
+        mg[j] = pp->data[j]->n;
+    }
+    j = getarg(vc,"what",0);
+    pp->wh = (j>0) ? ppwhat(argval(vc,j)) : PCOEF;
+    
+    preplot(&lf,&des,data,vdptr(pp->fit),vdptr(pp->se),band,mg,pp->gr,pp->wh);
+}
+
+INT setpppoints(pp,where,mg,xl)
+pplot *pp;
+char *where;
+INT *mg;
+double *xl;
+{ INT d, i, j, n, m;
+    varname vn;
+    d = pp->d = lf.mi[MDIM];
+    if (strcmp(where,"fitp")==0)
+    { n = lf.nv;
+        for (j=0; j<d; j++)
+        { sprintf(vn,"_pred%d",j);
+            pp->data[j] = createvar(vn,STPLOTVAR,n,VDOUBLE);
+            if (lf_error) return(0);
+            for (i=0; i<n; i++)
+                vassn(pp->data[j],i,evptx(&lf,i,j));
+        }
+        pp->gr = 4;
+        return(n);
+    }
+    if (strcmp(where,"data")==0)
+    { recondat(1,&n);
+        for (j=0; j<d; j++)
+        { sprintf(vn,"_pred%d",j);
+            pp->data[j] = createvar(vn,STPLOTVAR,n,VDOUBLE);
+            if (lf_error) return(0);
+            for (i=0; i<n; i++)
+                vassn(pp->data[j],i,datum(&lf,j,i));
+        }
+        pp->gr = 3;
+        return(n);
+    }
+    if (strcmp(where,"grid")==0)
+    { n = 1;
+        for (j=0; j<d; j++)
+        { sprintf(vn,"_pred%d",j);
+            m = (mg==NULL) ? 40 : mg[j];
+            pp->data[j] = createvar(vn,STPLOTVAR,m,VDOUBLE);
+            if (lf_error) return(0);
+            if (m==1)
+                vassn(pp->data[j],0,(xl[d+j]+xl[j])/2);
+            else
+                for (i=0; i<m; i++)
+                    vassn(pp->data[j],i,xl[j]+i*(xl[d+j]-xl[j])/(m-1));
+            n *= m;
+            pp->gr = 2;
+        }
+        return(n);
+    }
+    ERROR(("setpppoints: invalid where=%s",where));
+    return(0);
+}
+
+#endif
diff --git a/src/locfit/random.c b/src/locfit/random.c
new file mode 100644
index 0000000..70af7f5
--- /dev/null
+++ b/src/locfit/random.c
@@ -0,0 +1,147 @@
+/*
+ *   Copyright (c) 1996-2000 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include "local.h"
+
+#define PI_HALF         1.5707963267948966192313216916397514420986 /*pi/2*/
+#define PI_QUARTER      0.7853981633974483096156608458198757210493 /*pi/4*/
+#define EXP78           1.0129030479320018583185514777512982888868 /*e^(1/78)*/
+#define PI128          40.7436654315252059568342434233636766808217 /*128/pi*/
+
+static unsigned long cc, tv, ss=1;
+
+double runif()
+{	if (ss)
+	{ WARN(("runif: No seed set."));
+          return(0.0);
+	}
+        cc = cc * 69069;    /* congruential part */
+        tv ^= tv >> 15;       /* tausworthe part */
+        tv ^= tv << 17;
+	return(((tv ^ cc) >> 1) / 2147483648.0);
+}
+
+void rseed(seed)
+/*
+  Seed should be string of at least 8 characters.
+*/
+char *seed;
+{	ss = 0;
+	tv = seed[0];
+        tv = (tv<<8) | seed[1];
+        tv = (tv<<8) | seed[2];
+        tv = (tv<<8) | seed[3];
+ 	cc = seed[4];
+        cc = (cc<<8) | seed[5];
+        cc = (cc<<8) | seed[6];
+        cc = (cc<<8) | seed[7];
+        if(cc % 2 == 0)
+             cc++;
+}
+
+/*
+ * Gaussian random variable.
+ * Reference: Kinderman & Monahan, Proceedings of
+ * the ASA, Statistical Computing Section, 1975, 128-131.
+ */
+double rnorm(mu,s)
+double mu, s;
+{
+	double rnormk, u, x2;
+
+	do {
+		u = runif();
+		rnormk = 1.715527769 * (runif()-0.5) / u;
+		x2 = rnormk * rnormk / 4;
+	} while((x2>1-u) || (x2 > -log(u)));
+	return(mu+s*rnormk);
+}
+
+double rexp(lb)
+double lb;
+{ return(-log(runif())/lb);
+}
+
+/*
+ * Poisson random variable.
+ * Simple algorithm for small lambda, else complex algorithm.
+ * Crossover point must be at least 5 for the complex algorithm
+ * to work correctly.
+ * Reference: Devroye, pages 504, 511 and 516 (with corrections!)
+ */
+double rpois(lambda)
+double lambda;
+{
+	static double olambda = -1, a, mu, delta, d, c1, c2, c3, c4, c5;
+	double u, e, n, x, y, w, t, p, q;
+	int new = lambda != olambda;
+
+	olambda = lambda;
+	if(lambda < 8) {
+		if(new)
+			a = exp(-lambda);
+		q = 1;
+		x = -1;
+		do {
+			q *= runif();
+			x++;
+		} while(q >= a);
+		return(x);
+	}
+
+	if(new) {
+		mu = floor(lambda);
+		delta = sqrt(2 * mu * log(mu * PI128));
+		delta = MAX(6.0, MIN(mu, floor(delta)));
+		d = 2*mu + delta;
+		c1 = sqrt(mu * PI_HALF);
+		c2 = c1 + sqrt(d * PI_QUARTER) * exp(1/d);
+		c3 = c2 + 1;
+		c4 = c3 + EXP78;
+		c5 = c4 + 2 * d * exp(-delta*(1+delta/2)/d) / delta;
+	}
+	while(1) {
+		u = c5 * runif();
+		e = -log(runif());
+		if(u <= c1) {
+			n = rnorm(0.0,1.0);
+			x = floor(-fabs(n) * sqrt(mu));
+			if(x < -mu)
+				continue;
+			w = n*n/2 + e + x*log(lambda/mu);
+		} else if(u <= c2) {
+			y = 1 + fabs(rnorm(0.0,1.0)) * sqrt(d/2);
+			x = ceil(y);
+			if(x > delta)
+				continue;
+			w = y*(y-2)/d + e + x*log(lambda/mu);
+		} else if(u <= c3) {
+			x = 0;
+			w = e;
+		} else if(u <= c4) {
+			x = 1;
+			w = e + log(lambda/mu);
+		} else {
+			y = delta - 2*d*log(runif())/delta;
+			x = ceil(y);
+			w = delta*(1+y/2)/d + e + x*log(lambda/mu);
+		}
+		w = -w;
+		t = x*(x+1) / (2*mu);
+		if(x >= 0 && w <= -t)
+			return(x+mu);
+		if(x < 0 && w > -t)
+			continue;
+		q = t * ((2*x+1)/(6*mu) - 1);
+		if(w > q)
+			continue;
+		p = x+1 <= 0 ? x+1 : 0;
+		p = q - t*t/(3*(mu+p));
+		if(w <= p)
+			return(x+mu);
+		if(w <= x*log(mu) - LGAMMA(mu+x+1) + LGAMMA(mu+1))
+			return(x+mu);
+	}
+}
diff --git a/src/locfit/readfile.c b/src/locfit/readfile.c
new file mode 100644
index 0000000..ac8be4b
--- /dev/null
+++ b/src/locfit/readfile.c
@@ -0,0 +1,81 @@
+/*
+ *   Copyright (c) 1996-2000 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *
+ *   Function to read and create Locfit variables from an ASCII file.
+ *
+ *   Command syntax:
+ *     locfit> readfile filename v1 v2 v3
+ *   filename is the file to be read (no default extension or path is added).
+ *   v1 v2 v3  etc are the names of the variables to create.
+ *
+ *   File format: The file should be a plain ASCII file organized
+ *     in matrix format, with one variable per column and one observation
+ *     per row. Fields are separated by spaces.
+ *     
+ */
+
+#include "local.h"
+
+extern char filename[];
+static FILE *aaa;
+
+void readfile(vc)
+vari *vc;
+{ int i, j, k, n, nv;
+  char wc[50], *fn;
+  double *dpr;
+  vari *v;
+
+  i = getarg(vc,"file",1);
+  if (i==0)
+  { ERROR(("readfile: no file"));
+    return;
+  }
+
+  fn = argval(vc,i);
+  setfilename(fn,"","r",0);
+  if (lf_error) return;
+
+  i = getarg(vc,"arith",0); /* now automatic - leave for backward compat. */
+
+  aaa = fopen(filename,"r");
+  v = createvar("readfile",STREADFI,0,VDOUBLE);
+
+  n = 0;
+  do
+  { k = fscanf(aaa,"%s",wc);
+    if (k==1)
+    { vassn(v,n,darith(wc));
+      n++;
+    }
+  } while (k==1);
+  fclose(aaa);
+  dpr = vdptr(v);
+  deletevar(v);
+
+  nv = 0;
+  for (i=1; i<vlength(vc); i++)
+    if (!argused(vc,i)) nv++;
+  if (nv==0) { ERROR(("readfile: no variables specified")); return; }
+  if (n%nv != 0)
+    WARN(("number of items not multiple of number of variables"));
+
+  n /= nv;
+  transpose(dpr,n,nv);
+  nv = 0;
+  for (i=1; i<vlength(vc); i++)
+    if (!argused(vc,i))
+    { v = createvar(argval(vc,i),STREGULAR,n,VDOUBLE);
+      if (v==NULL) return;
+      for (j=0; j<n; j++) vassn(v,j,dpr[nv*n+j]);
+      nv++;
+      if (lf_error) return;
+    }
+  if (argarg(vc,0)!=NULL)
+    dosavedata(vc,0);
+  else
+    for (i=1; i<vlength(vc); i++) setused(vc,i);
+}
diff --git a/src/locfit/scb.c b/src/locfit/scb.c
new file mode 100644
index 0000000..ff8ae3e
--- /dev/null
+++ b/src/locfit/scb.c
@@ -0,0 +1,326 @@
+/*
+ *   Copyright (c) 1996-2001 Jiayang Sun, Catherine Loader.
+ *   This file is used by the simultaneous confidence band
+ *   additions to Locfit.
+ *
+ */
+
+#include "local.h"
+extern int cvi;
+static double scb_crit, *x, c[10], kap[5], kaq[5], max_p2;
+static int type, side;
+
+double covar_par(lf,des,x1,x2)
+lfit *lf;
+design *des;
+double x1, x2;
+{ double *v1, *v2, *wk;
+  paramcomp *pc;
+  int i, j, p, ispar;
+
+  v1 = des->f1; v2 = des->ss; wk = des->oc;
+  ispar = (lf->mi[MKER]==WPARM) && (hasparcomp(lf));
+  p = lf->mi[MP];
+
+/*  for parametric models, the covariance is
+ *  A(x1)^T (X^T W V X)^{-1} A(x2)
+ *  which we can find easily from the parametric component.
+ */
+  if (ispar)
+  { pc = &lf->pc;
+    fitfun(lf,&x1,pc->xbar,v1,NULL,0);
+    fitfun(lf,&x2,pc->xbar,v2,NULL,0);
+    jacob_hsolve(&lf->pc.xtwx,v1);
+    jacob_hsolve(&lf->pc.xtwx,v2);
+  }
+
+/*  for non-parametric models, we must use the cholseky decomposition
+ *  of M2 = X^T W^2 V X. Courtesy of comp_vari, we already have
+ *  des->P = M2^{1/2} M1^{-1}.
+ */
+  if (!ispar)
+  { fitfun(lf,&x1,des->xev,wk,NULL,0);
+    for (i=0; i<p; i++)
+    { v1[i] = 0;
+      for (j=0; j<p; j++) v1[i] += des->P[i*p+j]*wk[j];
+    }
+    fitfun(lf,&x2,des->xev,wk,NULL,0);
+    for (i=0; i<p; i++)
+    { v2[i] = 0;
+      for (j=0; j<p; j++) v2[i] += des->P[i*p+j]*wk[j];
+    }
+  }
+
+  return(innerprod(v1,v2,p));
+}
+
+void cumulant(lf,des,sd)
+lfit *lf;
+design *des;
+double sd;
+{ double b2i, b3i, b3j, b4i;
+  double ss, si, sj, uii, uij, ujj, k1;
+  INT ii, i, j, jj, *mi;
+  for (i=1; i<10; i++) c[i] = 0.0;
+  k1 = 0;
+  mi = lf->mi;
+
+  /* ss = sd*sd; */
+  ss = covar_par(lf,des,des->xev[0],des->xev[0]);
+
+/*
+ * this isn't valid for nonparametric models. At a minimum,
+ * the sums would have to include weights. Still have to work
+ * out the right way.
+ */
+  for (i=0; i<mi[MN]; i++)
+  { ii = des->ind[i];
+    b2i = b2(des->th[i],mi[MTG],prwt(lf,ii));
+    b3i = b3(des->th[i],mi[MTG],prwt(lf,ii));
+    b4i = b4(des->th[i],mi[MTG],prwt(lf,ii));
+    si = covar_par(lf,des,des->xev[0],datum(lf,0,ii));
+    uii= covar_par(lf,des,datum(lf,0,ii),datum(lf,0,ii));
+    if (lf_error) return;
+
+    c[2] += b4i*si*si*uii;
+    c[6] += b4i*si*si*si*si;
+    c[7] += b3i*si*uii;
+    c[8] += b3i*si*si*si;
+    /* c[9] += b2i*si*si*si*si;
+       c[9] += b2i*b2i*si*si*si*si; */
+    k1 += b3i*si*(si*si/ss-uii);
+
+    /* i=j components */
+    c[1] += b3i*b3i*si*si*uii*uii;
+    c[3] += b3i*b3i*si*si*si*si*uii;
+    c[4] += b3i*b3i*si*si*uii*uii;
+
+    for (j=i+1; j<mi[MN]; j++)
+    { jj = des->ind[j];
+      b3j = b3(des->th[j],mi[MTG],prwt(lf,jj));
+      sj = covar_par(lf,des,des->xev[0],datum(lf,0,jj));
+      uij= covar_par(lf,des,datum(lf,0,ii),datum(lf,0,jj));
+      ujj= covar_par(lf,des,datum(lf,0,jj),datum(lf,0,jj));
+
+      c[1] += 2*b3i*b3j*si*sj*uij*uij;
+      c[3] += 2*b3i*b3j*si*si*sj*sj*uij;
+      c[4] += b3i*b3j*uij*(si*si*ujj+sj*sj*uii);
+      if (lf_error) return;
+    }
+  }
+  c[5] = c[1];
+  c[7] = c[7]*c[8];
+  c[8] = c[8]*c[8];
+
+  c[1] /= ss; c[2] /= ss; c[3] /= ss*ss; c[4] /= ss;
+  c[5] /= ss; c[6] /= ss*ss; c[7] /= ss*ss;
+  c[8] /= ss*ss*ss; c[9] /= ss*ss;
+
+/* constants used in p(x,z) computation */
+  kap[1] = k1/(2*sqrt(ss));
+  kap[2] = 1 + 0.5*(c[1]-c[2]+c[4]-c[7]) - 3*c[3] + c[6] + 1.75*c[8];
+  kap[4] = -9*c[3] + 3*c[6] + 6*c[8] + 3*c[9];
+
+/* constants used in q(x,u) computation */
+  kaq[2] = c[3] - 1.5*c[8] - c[5] - c[4] + 0.5*c[7] + c[6] - c[2];
+  kaq[4] = -3*c[3] - 6*c[4] - 6*c[5] + 3*c[6] + 3*c[7] - 3*c[8] + 3*c[9];
+}
+
+/* q2(u) := u+q2(x,u) in paper */
+double q2(u)
+double u;
+{ return(u-u*(36.0*kaq[2] + 3*kaq[4]*(u*u-3) + c[8]*((u*u-10)*u*u+15))/72.0);
+}
+
+/*  p2(u) := p2(x,u) in paper */
+double p2(u)
+double u;
+{ return( -u*( 36*(kap[2]-1+kap[1]*kap[1])
+     + 3*(kap[4]+4*kap[1]*sqrt(kap[3]))*(u*u-3)
+     + c[8]*((u*u-10)*u*u+15) ) / 72 );
+}
+
+void procvscb2(des,lf,v)
+design *des;
+lfit *lf;
+INT v;
+{ double thhat, sd, *lo, *hi, u;
+  int err, tmp;
+  x = des->xev = evpt(lf,v);
+  tmp = lf->mi[MPC];
+  if ((lf->mi[MKER]==WPARM) && (hasparcomp(lf)))
+  { lf->coef[v] = thhat = addparcomp(lf,des->xev,PCOEF);
+    lf->nlx[v] = sd = addparcomp(lf,des->xev,PNLX);
+  }
+  else
+  { lf->mi[MPC] = 0;
+    procv(des,lf,v);
+    thhat = lf->coef[v];
+    sd = lf->nlx[v];
+  }
+  if (type >= 2)
+  { if (lf->mi[MKER] != WPARM)
+      WARN(("nonparametric fit; correction is invalid"));
+    cumulant(lf,des,sd);
+  }
+  lf->mi[MPC] = tmp;
+  lo = vdptr(lf->L);
+  hi = &lo[lf->nvm];
+  switch(type)
+  { case 0:
+    case 1: /* basic scr */
+      lo[v] = thhat - scb_crit * sd;
+      hi[v] = thhat + scb_crit * sd;
+      return;
+    case 2: /* centered scr */
+      lo[v] = thhat - kap[1]*sd - scb_crit*sd*sqrt(kap[2]);
+      hi[v] = thhat - kap[1]*sd + scb_crit*sd*sqrt(kap[2]);
+      return;
+    case 3: /* corrected 2 */
+      u = solve_secant(q2,scb_crit,0.0,2*scb_crit,0.000001,BDF_NONE,&err);
+      lo[v] = thhat - u*sd;
+      hi[v] = thhat + u*sd;
+      return;
+    case 4: /* corrected 2' */
+      u = fabs(p2(scb_crit));
+      max_p2 = MAX(max_p2,u);
+      lo[v] = thhat;
+      hi[v] = thhat;
+      return;
+  }
+  ERROR(("procvscb2: invalid type"));
+}
+
+void scb(des,lf)
+design *des;
+lfit *lf;
+{ double kap[10], *lo, *hi;
+  INT i, *mi, nterms;
+  mi = lf->mi;
+  mi[MP] = calcp(mi,mi[MDEG]);
+  type = mi[MGETH] - 70;
+  deschk(des,mi[MN],mi[MP]);
+  des->pref = 0;
+  cvi = -1; /* inhibit cross validation */
+  mi[MLINK] = defaultlink(mi[MLINK],mi[MTG]);
+
+  if (type==0)
+  { kap[0] = 1;
+    scb_crit = critval(kap,1,0,0.05,10,2,0.0);
+  }
+  else
+  { compparcomp(des,lf,0);
+    nterms = constants(des,lf,kap);
+    scb_crit = critval(kap,nterms,mi[MDIM],0.05,10,2,0.0);
+  }
+
+  max_p2 = 0.0;
+  startlf(des,lf,procvscb2,0);
+  if (type==4)
+  { lo = vdptr(lf->L);
+    hi = &lo[lf->nvm];
+    for (i=0; i<lf->nv; i++)
+    {
+      lo[i] -= (scb_crit-max_p2)*lf->nlx[i];
+      hi[i] += (scb_crit-max_p2)*lf->nlx[i];
+    }
+  }
+}
+
+#ifdef CVERSION
+extern lfit lf;
+extern design des;
+extern vari *aru;
+
+lfit *lf_sim;
+design *des_sim;
+
+double scbsim_fun(x)
+double x;
+{ double y;
+  evptx(lf_sim,0,0) = x;
+  procv(des_sim,lf_sim,0);
+
+  if (type>=2)
+  { if (lf_sim->mi[MKER] != WPARM)
+      WARN(("nonparametric fit; correction is invalid"));
+    cumulant(lf_sim,des_sim,lf_sim->nlx[0]);
+  }
+
+  y = lf_link(dareval(aru,0,&x),lf_sim->mi[MLINK]);
+  y = (lf_sim->coef[0] - y) / lf_sim->nlx[0];
+
+  switch(type)
+  {
+    case 2:
+      y = (y-kap[1]) / sqrt(kap[2]);
+      break;
+    case 3:
+      y = (y-kap[1])/sqrt(kap[2]);
+      y = (y>0) ? y+q2(y) : y - q2(y);
+      break;
+  }
+
+  switch(side)
+  { case -1: return(-y);
+    case  1: return(y);
+    default: return(fabs(y));
+  }
+}
+
+static double max;
+
+void do_scbsim(des,lf)
+design *des;
+lfit *lf;
+{ double y;
+  int err;
+
+  lf_sim = lf;
+  des_sim = des;
+
+  trchck(lf,1,1,lf->mi[MDIM],lf->mi[MP],1);
+  y = max_quad(scbsim_fun,lf->fl[0],lf->fl[1],10,0.00001,&err,'y');
+  max = y;
+}
+
+void scbsim(lf,des)
+lfit *lf;
+design *des;
+{ double kap[5];
+  int nterms;
+
+  lf->mi[MEV] = 100;
+  startlf(des,lf,scbsim_fun,1);
+
+  nterms = constants(des,lf,kap);
+  printf("xmx: %10.6f  max: %10.6f  k0 %10.6f %10.6f  pr %10.6f\n",0.0,max,kap[0],kap[1],tailp(max,kap,nterms,lf->mi[MDIM],0.0));
+}
+
+void cscbsim(v)
+vari *v;
+{ int i;
+  side = 0; type = 1;
+  fitoptions(&lf,v,0);
+
+  i = getarg(v,"mean",1);
+  if (i==0)
+  { WARN(("cscbsim: no mean function; setting = 0"));
+    aru = arbuild("0",0,0,NULL,0,1);
+  }
+  else
+  { aru = arbuild(argval(v,i),0,strlen(argval(v,i))-1,NULL,0,1);
+    setvarname(aru,"_aru");
+  }
+
+  i = getarg(v,"corr",1);
+  if (i>0) type = getlogic(v,i);
+  if (lf_error) return;
+
+  i = getarg(v,"side",1);
+  if (i>0) sscanf(argval(v,i),"%d",&side);
+  if (lf_error) return;
+
+  scbsim(&lf,&des);
+}
+#endif
diff --git a/src/locfit/scb_cons.c b/src/locfit/scb_cons.c
new file mode 100644
index 0000000..49c8a04
--- /dev/null
+++ b/src/locfit/scb_cons.c
@@ -0,0 +1,342 @@
+/*
+ *   Copyright (c) 1996-2001 Jiayang Sun, Catherine Loader.
+ *   This file is used by the simultaneous confidence band
+ *   additions to Locfit.
+ *
+ */
+
+#include "local.h"
+
+extern INT cvi;
+
+static double *fd, *ft, *lij, *d1a;
+static INT par;
+
+void assignk0(z,d,n) /* z should be n*(2*d*d+2*d+2); */
+double *z;
+INT d, n;
+{ d1a= z; z += d*d*n;
+  ft = z; z += n*(d*(d+1)+1);
+  fd = z; z += n*(d+1);
+}
+
+void christ(d,nn,nl)  /* lij[i][j] = res proj. of Tij to (T1...Td) */
+double nl;
+INT d, nn;
+{ INT i, j, k, l;
+  double p4, *ll, v[1+MXDIM];
+  for (i=0; i<d; i++)
+    for (j=i; j<d; j++)
+    { ll = &lij[(i*d+j)*nn];
+      for (k=0; k<=d; k++) v[k] = innerprod(&ft[k*nn],ll,nn);
+      bacT(fd,v,d+1,0,d+1);
+      for (k=0; k<nn; k++)
+        for (l=0; l<=d; l++)
+          ll[k] -= ft[l*nn+k]*v[l];
+      p4 = 0;
+      for (k=0; k<=i+1; k++)
+        p4 += fd[k*(d+1)+i+1]*fd[k*(d+1)+j+1];
+      p4 = (fd[i+1]*fd[j+1]-p4)/(nl*nl);
+      for (k=0; k<nn; k++)
+        ll[k] = lij[(j*d+i)*nn+k] = ll[k] + p4*ft[k];
+    }
+}
+
+void d1(n,d)   /* d1[i][j] = e_i^T (A^T A)^{-1} B_j^T */
+INT n, d;
+{ INT a, b, i, j;
+  double *dd, v[MXDIM];
+  for (i=0; i<d; i++)
+  { for (j=0; j<d; j++) v[j] = 0;
+    v[i] = 1;
+    bacT(fd,v,d+1,1,d+1);
+    for (j=0; j<d; j++)
+    { dd = &d1a[(i*d+j)*n];
+      for (a=0; a<n; a++)
+      { dd[a] = 0;
+        for (b=0; b<d; b++)
+          dd[a] += v[b] * lij[(j*d+b)*n+a]; 
+} } } }
+
+void k2x(lf,des,kap)
+lfit *lf;
+design *des;
+double *kap;
+{ double det, s;
+  INT i, j, k, l, d, m;
+  d = lf->mi[MDIM];
+  m = wdiag(lf,des,ft,1+(d>1),2,0);
+  lij = &ft[(d+1)*m];
+  for (i=0; i<m; i++)
+    for (j=0; j<=d; j++)
+      fd[i*(d+1)+j] = ft[j*m+i];
+  QR1(fd,m,d+1,NULL);
+  s = 0;
+  if (d>1)
+  { christ(d,m,fd[0]);
+    d1(m,d);
+    for (j=0; j<d; j++)
+      for (k=0; k<j; k++)
+        for (l=0; l<m; l++)
+          s += d1a[(j*d+k)*m+l]*d1a[(k*d+j)*m+l]
+             - d1a[(j*d+j)*m+l]*d1a[(k*d+k)*m+l];
+  }
+  det = 1;
+  for (j=1; j<=d; j++)
+    det *= fd[j*(d+2)]/fd[0];
+  kap[0] = det;
+  kap[2] = s*det*fd[0]*fd[0];
+}
+
+void l1x(lf,des,lap,re)
+lfit *lf;
+design *des;
+double *lap;
+INT re;
+{ double det, t, sumcj, nu, *u, v[MXDIM];
+  INT i, j, j1, d, m;
+  d = lf->mi[MDIM]; u = des->res;
+  m = wdiag(lf,des,ft,2,2,0);
+  lij = &ft[(d+1)*m];
+  for (i=0; i<m; i++)
+  { t = ft[(re+1)*m+i];
+    ft[(re+1)*m+i] = ft[d*m+i];
+    ft[d*m+i] = t;
+    for (j=0; j<d; j++) /* don't copy last column */
+      fd[i*d+j] = ft[j*m+i];
+    u[i] = ft[d*m+i];
+  }
+  QR1(fd,m,d,&ft[d*m]);
+  bacK(fd,&ft[d*m],d);
+  nu = 0;
+  for (i=0; i<m; i++)
+  { for (j=0; j<d; j++)
+      u[i] -= ft[j*m+i]*ft[d*m+j];
+    nu += u[i]*u[i];
+  }    /* now u is outward vector, nu = ||u|| */
+  sumcj = 0;
+  for (i=0; i<d; i++) /* copy l(d-1,i) to l(re,i) */
+    for (j=0; j<m; j++)
+      lij[(re*d+i)*m+j] = lij[((d-1)*d+i)*m+j];
+  for (j=0; j<d; j++)
+  { if (j != re)
+    { j1 = (j==(d-1)) ? re : j;
+      for (i=0; i<d-1; i++)
+        v[i] = innerprod(&lij[(i*d+j)*m],u,m);
+      bacT(fd,v,d,1,d);
+      sumcj += -v[j1];
+    }
+  }                                   /* stage 3,4 now complete */
+  det = 1;
+  for (j=1; j<d; j++)
+    det *= fd[j*(d+1)]/fd[0];
+  lap[0] = det;
+  lap[1] = sumcj*det*fd[0]/sqrt(nu);
+}
+
+void m0x(lf,des,m0,re,rg)
+lfit *lf;
+design *des;
+double *m0;
+INT re, rg;
+{ double det, t;
+  INT d, m, i, j;
+  d = lf->mi[MDIM];
+  m = wdiag(lf,des,ft,1,2,0);
+  for (i=0; i<m; i++)
+  { t=ft[(rg+1)*m+i]; ft[(rg+1)*m+i]=ft[d*m+i]; ft[d*m+i]=t;
+    t=ft[(re+1)*m+i]; ft[(re+1)*m+i]=ft[(d-1)*m+i]; ft[(d-1)*m+i]=t;
+    for (j=0; j<=d; j++)
+      fd[i*(d+1)+j] = ft[j*m+i];
+  }
+  det = 1;
+  QR1(fd,m,d+1,NULL);
+  for (j=1; j<d-1; j++)
+    det *= fd[j*(d+2)]/fd[0];
+  m0[0] = det*atan2(fd[d*(d+2)],-par*fd[d*(d+1)-1]);
+}
+
+INT constants(des,lf,kap)
+design *des;
+lfit *lf;
+double *kap;
+{ double h, k0[3], k1[3], l0[2], l1[2], m0[1], m1[1];
+  double z[MXDIM], delt[MXDIM], mk, ml, mm;
+  INT d, i, j, nnn, wt, index[MXDIM], *mi, pe, re, rg;
+  cvi = -1; /* avoid cross valid */
+  mi = lf->mi;
+  d = mi[MDIM];
+  if (lf_error) return(0);
+  if ((lf->mi[MKER] != WPARM) && (lf->dp[DALP]>0))
+    WARN(("constants are approximate for varying h"));
+  mi[MP] = calcp(mi,mi[MDEG]);
+  deschk(des,mi[MN],mi[MP]);
+  preproc(des,lf,mi[MKER]!=WPARM);
+  nnn = (ident==1) ? lf->mi[MP] : lf->mi[MN];
+  lf->L = checkvarlen(lf->L,2*nnn*(d*d+d+1),"_hatmat",VDOUBLE);
+  assignk0(vdptr(lf->L),d,nnn);
+  mi[MDC] = 1;
+  des->xev = z;
+
+  mk = 1.0;
+  for (i=0; i<d; i++)
+  { index[i] = 0;
+    z[i] = lf->fl[i];
+    delt[i] = (lf->fl[i+d]-z[i])/(3*mi[MMINT]);
+    mk *= delt[i];
+  }
+  i = 0;
+  
+  k0[0] = k0[1] = k0[2] = 0.0;
+  l0[0] = l0[1] = 0.0;
+  m0[0] = 0.0;
+
+#ifdef CVERSION
+  if (mi[MIT]==IMONT)
+  { for (i=0; i<mi[MMINT]; i++)
+    { for (j=0; j<d; j++) z[j] = lf->fl[j]+(lf->fl[j+d]-lf->fl[j])*runif();
+      if ((mi[MKER]!=WPARM) | (!hasparcomp(lf)))
+      { h = nbhd(lf,des,(INT)(mi[MN]*lf->dp[DALP]),lf->dp[DFXH],0);
+        locfit(lf,des,h,1);
+      }
+      k2x(lf,des,k1);
+      k0[0] += k1[0];
+    }
+    for (j=0; j<d; j++) k0[0] *= lf->fl[j+d]-lf->fl[j];
+    kap[0] = k0[0]/mi[MMINT];
+    return(1);
+  }
+#endif
+
+  while(1)
+  {
+    wt = 1;
+    for (i=0; i<d; i++)
+      wt *= (4-2*(index[i]%2==0)-(index[i]==0)-(index[i]==mi[MMINT]));
+    if ((mi[MKER]!=WPARM) | (!hasparcomp(lf)))
+    { h = nbhd(lf,des,(INT)(mi[MN]*lf->dp[DALP]),lf->dp[DFXH],0);
+      locfit(lf,des,h,1);
+    }
+    k2x(lf,des,k1);
+    k0[0] += wt*mk*k1[0];
+    k0[2] += wt*mk*k1[2];
+
+    for (re=0; re<d; re++) if ((index[re]==0) | (index[re]==mi[MMINT]))
+    { l1x(lf,des,l1,re);
+      ml = 1;
+      for (i=0; i<d; i++) if (i!=re) ml *= delt[i];
+      pe = 1-2*(index[re]==0);
+      l0[0] += wt*ml*l1[0];
+      l0[1] += wt*ml*pe*l1[1];
+
+      for (rg=re+1; rg<d; rg++) if ((index[rg]==0) | (index[rg]==mi[MMINT]))
+      { par = pe*(1-2*(index[rg]==0));
+        m0x(lf,des,m1,re,rg);
+        mm = 1;
+        for (i=0; i<d; i++) if ((i!=re) & (i!=rg)) mm *= delt[i];
+        m0[0] += wt*mm*m1[0];
+      }
+    }
+
+    /* compute next grid point */
+    for (i=0; i<d; i++)
+    { index[i]++;
+      z[i] = lf->fl[i]+3*delt[i]*index[i];
+      if (index[i]>mi[MMINT])
+      { index[i] = 0;
+        z[i] = lf->fl[i];
+        if (i==d-1) /* done */
+        { kap[0] = k0[0];
+          kap[1] = l0[0]/2;
+          if (d==1) return(2);
+          k0[2] = -k0[2] - d*(d-1)*k0[0]/2;
+          if (mi[MDEB]>0)
+          { printf("constants:\n");
+            printf("  k0: %8.5f  k2: %8.5f\n",k0[0],k0[2]);
+            printf("  l0: %8.5f  l1: %8.5f\n",l0[0],l1[1]);
+            printf("  m0: %8.5f\n",m0[0]);
+            printf("  check: %8.5f\n",(k0[0]+k0[2]+l0[1]+m0[0])/(2*PI));
+          }
+          kap[2] = (k0[2]+l0[1]+m0[0])/(2*PI);
+          return(3);
+        }
+      }
+      else i = d;
+    }
+
+  }
+}
+
+double tailp(c,k0,m,d,nu)
+double c, *k0, nu;
+INT m, d;
+{ INT i;
+  double p;
+  p = 0;
+  if (nu==0)
+  { for (i=0; i<m; i++) if (k0[i]>0)
+      p += k0[i]*exp(LGAMMA((d+1-i)/2.0)-(d+1-i)*LOGPI/2)
+          *(1-pchisq(c*c,(double) d+1-i));
+  }
+  else
+  { for (i=0; i<m; i++) if (k0[i]>0)
+      p += k0[i]*exp(LGAMMA((d+1-i)/2.0)-(d+1-i)*LOGPI/2)
+          *(1-pf(c*c/(d+1-i),(double) (d+1-i), nu));
+  }
+  return(p);
+}
+
+double taild(c,k0,m,d,nu)
+double c, *k0, nu;
+INT m, d;
+{ double p;
+  INT i;
+  p = 0;
+  if (nu==0)
+  { for (i=0; i<m; i++) if (k0[i]>0)
+      p += k0[i]*exp(LGAMMA((d+1-i)/2.0)-(d+1-i)*LOGPI/2)
+          *2*c*dchisq(c*c,(double) (d+1-i));
+  }
+  else
+  { for (i=0; i<m; i++) if (k0[i]>0)
+      p += k0[i]*exp(LGAMMA((d+1-i)/2.0)-(d+1-i)*LOGPI/2)
+          *2*c*df(c*c/(d+1-i),(double) (d+1-i), nu)/(d+1-i);
+  }
+  return(-p);
+}
+
+double critval(k0,m,d,al,it,s,nu)
+double *k0, al, nu;
+INT m, d, it, s;
+{ double c, cn, c0, c1, tp, td;
+  INT j;
+  if (m<0) ERROR(("critval: no terms?"));
+  if (m>d+1) m = d+1;
+  if ((al<=0) | (al>=1)) ERROR(("critval: invalid alpha %8.5f",al));
+  if (lf_error) return(0.0);
+  if (al>0.5) WARN(("critval: A mighty large tail probability al=%8.5f",al));
+  if (s==1) al = 2*al;
+  if (m==0) { d = 0; k0[0] = 1; m = 1; }
+  c = 2.0; c0 = 0.0; c1 = 0.0;
+  for (j=0; j<it; j++)
+  { tp = tailp(c,k0,m,d,nu)-al;
+    td = taild(c,k0,m,d,nu);
+    if (tp>0) c0 = c;
+    if (tp<0) c1 = c;
+    cn = c - tp/td;
+    if (cn<c0) cn = (c+c0)/2;
+    if ((c1>0.0) && (cn>c1)) cn = (c+c1)/2;
+    c = cn;
+    if (fabs(tp/al)<1.0e-10) return(c);
+  }
+  return(c);
+}
+
+#ifdef SVERSION
+void scritval(k0,d,cov,m,rdf,z)
+double *k0, *z, *cov, *rdf;
+INT *d, *m;
+{ lf_error = 0;
+  *z = critval(k0,*m,*d,1-*cov,10,2,*rdf);
+}
+#endif
diff --git a/src/locfit/simul.c b/src/locfit/simul.c
new file mode 100644
index 0000000..b26c23c
--- /dev/null
+++ b/src/locfit/simul.c
@@ -0,0 +1,219 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ */
+
+#include "local.h"
+
+static double pen, sig2;
+
+void goldensec(f,des,tr,eps,xm,ym,meth)
+double (*f)(), eps, *xm, *ym;
+INT meth;
+design *des;
+lfit *tr;
+{ double x[4], y[4], xx[11], yy[11];
+  INT i, im;
+  xx[0] = tr->dp[DFXH];
+  if (xx[0]<=0)
+  { ERROR(("regband: initialize h>0"));
+    return;
+  }
+  for (i=0; i<=10; i++)
+  { if (i>0) xx[i] = (1+GOLDEN)*xx[i-1];
+    yy[i] = f(xx[i],des,tr,meth);
+    if ((i==0) || (yy[i]<yy[im])) im = i;
+  }
+  if (im==0) im = 1;
+  if (im==10)im = 9;
+  x[0] = xx[im-1]; y[0] = yy[im-1];
+  x[1] = xx[im];   y[1] = yy[im];
+  x[3] = xx[im+1]; y[3] = yy[im+1];
+  x[2] = GOLDEN*x[3]+(1-GOLDEN)*x[0];
+  y[2] = f(x[2],des,tr,meth);
+  while (x[3]-x[0]>eps)
+  { if (y[1]<y[2])
+    { x[3] = x[2]; y[3] = y[2];
+      x[2] = x[1]; y[2] = y[1];
+      x[1] = GOLDEN*x[0]+(1-GOLDEN)*x[3];
+      y[1] = f(x[1],des,tr,meth);
+    }
+    else
+    { x[0] = x[1]; y[0] = y[1];
+      x[1] = x[2]; y[1] = y[2];
+      x[2] = GOLDEN*x[3]+(1-GOLDEN)*x[0];
+      y[2] = f(x[2],des,tr,meth);
+    }
+  }
+  im = 0;
+  for (i=1; i<4; i++) if (y[i]<y[im]) im = i;
+  *xm = x[im]; *ym = y[im];
+}
+
+double dnk(x,k)
+double x;
+INT k;
+{ double f;
+  switch(k)
+  { case 0: f = 1; break;
+    case 1: f = -x; break;
+    case 2: f = x*x-1; break;
+    case 3: f = x*(x*x-3); break;
+    case 4: f = 3-x*x*(6-x*x); break;
+    case 5: f = -x*(15-x*x*(10-x*x)); break;
+    case 6: f = -15+x*x*(45-x*x*(15-x*x)); break;
+    default: ERROR(("dnk: k=%d too large",k)); return(0.0);
+  }
+  return(f*exp(-x*x/2)/S2PI);
+}
+
+double locai(h,des,tr)
+double h;
+design *des;
+lfit *tr;
+{ double cp;
+  tr->dp[DALP] = h;
+  startlf(des,tr,procv,0);
+  ressumm(tr,des);
+  cp = -2*tr->dp[DLK]+pen*tr->dp[DT0];
+  return(cp);
+}
+
+double loccp(h,des,tr,m) /* m=1: cp    m=2: gcv */
+double h;
+design *des;
+lfit *tr;
+int m;
+{ double cp;
+  INT dg;
+  tr->dp[DALP] = 0;
+  tr->dp[DFXH] = h;
+  dg = tr->mi[MDEG]; tr->mi[MDEG] = tr->mi[MDEG0];
+  startlf(des,tr,procv,0);
+  ressumm(tr,des);
+  if (m==1)
+    cp = -2*tr->dp[DLK]/sig2 - tr->mi[MN] + 2*tr->dp[DT0];
+  else cp = -2*tr->mi[MN]*tr->dp[DLK]/((tr->mi[MN]-tr->dp[DT0])*(tr->mi[MN]-tr->dp[DT0]));
+  printf("h %8.5f  deg %2d  rss %8.5f  trl %8.5f  cp: %8.5f\n",h,tr->mi[MDEG],-2*tr->dp[DLK],tr->dp[DT0],cp);
+  tr->mi[MDEG0] = tr->mi[MDEG]; tr->mi[MDEG] = dg;
+  return(cp);
+}
+
+double cp(des,tr,meth)
+design *des;
+lfit *tr;
+INT meth;
+{ double hm, ym;
+  goldensec(loccp,des,tr,0.001,&hm,&ym,meth);
+  return(hm);
+}
+
+double gkk(des,tr)
+design *des;
+lfit *tr;
+{ double h, h5, nf, th;
+  INT i, j, n, dg0, dg1;
+  tr->mi[MEV]=EDATA;
+  tr->dp[DALP] = 0;
+  n = tr->mi[MN];
+  dg0 = tr->mi[MDEG0];     /* target degree */
+  dg1 = dg0+1+(dg0%2==0);  /* pilot degree */
+  nf = exp(log(1.0*n)/10); /* bandwidth inflation factor */
+  h = tr->dp[DFXH];        /* start bandwidth */
+  for (i=0; i<=10; i++)
+  { tr->mi[MDEG] = dg1;
+    tr->dp[DFXH] = h*nf;
+    startlf(des,tr,procv,0);
+    th = 0;
+    for (j=10; j<n-10; j++)
+      th += tr->coef[dg1*n+j]*tr->coef[dg1*n+j];
+th *= n/(n-20.0);
+    h5 = sig2*Wikk(tr->mi[MKER],dg0)/th;
+    h = exp(log(h5)/(2*dg1+1));
+/* printf("pilot %8.5f  sel %8.5f\n",tr->dp[DFXH],h); */
+  }
+  return(h);
+}
+
+double rsw(des,tr,kk)
+design *des;
+lfit *tr;
+INT *kk;
+{ INT i, j, k, nmax, nvm, n, mk, ev, dg0, dg1;
+  double rss[6], cp[6], th22, dx, d2, hh;
+  nmax = 5;
+  ev = tr->mi[MEV];  tr->mi[MEV] = EGRID;
+  mk = tr->mi[MKER]; tr->mi[MKER]= WRECT;
+  dg0 = tr->mi[MDEG0];
+  dg1 = 1 + dg0 + (dg0%2==0);
+  tr->mi[MDEG]= 4;
+  for (k=nmax; k>0; k--)
+  { tr->mg[0] = k;
+    tr->fl[0] = 1.0/(2*k); tr->fl[1] = 1-1.0/(2*k);
+    tr->dp[DALP] = 0; tr->dp[DFXH] = 1.0/(2*k);
+    startlf(des,tr,procv,0);
+    nvm = tr->nvm;
+    rss[k] = 0;
+    for (i=0; i<k; i++) rss[k] += -2*tr->lik[i];
+  }
+  n = tr->mi[MN]; k = 1;
+  for (i=1; i<=nmax; i++)
+  { /* cp[i] = (n-5*nmax)*rss[i]/rss[nmax]-(n-10*i); */
+    cp[i] = rss[i]/sig2-(n-10*i);
+    if (cp[i]<cp[k]) k = i;
+  }
+  *kk = k;
+  tr->mg[0] = k;
+  tr->fl[0] = 1.0/(2*k); tr->fl[1] = 1-1.0/(2*k);
+  tr->dp[DALP] = 0; tr->dp[DFXH] = 1.0/(2*k);
+  startlf(des,tr,procv,0);
+  tr->mi[MKER] = mk; tr->mi[MEV] = ev;
+  nvm = tr->nvm;
+  th22 = 0;
+  for (i=10; i<n-10; i++)
+  { j = floor(k*datum(tr,0,i));
+    if (j>=k) j = k-1;
+    dx = datum(tr,0,i)-evptx(tr,0,j);
+    if (dg1==2)
+      d2 = tr->coef[2*nvm+j]+dx*tr->coef[3*nvm+j]+dx*dx*tr->coef[4*nvm+j]/2;
+    else d2 = tr->coef[4*nvm+j];
+    th22 += d2*d2;
+  }
+  hh = Wikk(mk,dg0)*sig2/th22*(n-20.0)/n;
+  return(exp(log(hh)/(2*dg1+1)));
+}
+
+void rband(des,tr,hhat,meth,nmeth,kk)
+design *des;
+lfit *tr;
+double *hhat;
+INT *meth, *nmeth, *kk;
+{ INT i, deg;
+  double h0;
+
+  /* first, estimate sigma^2 */
+  deg = tr->mi[MDEG]; tr->mi[MDEG] = 2;
+  h0 = tr->dp[DFXH];  tr->dp[DFXH] = 0.05;
+printf("alp: %8.5f  h: %8.5f  deg %2d  ev %2d\n",tr->dp[DALP],tr->dp[DFXH],tr->mi[MDEG],tr->mi[MEV]);
+  startlf(des,tr,procv,0);
+  ressumm(tr,des);
+  tr->mi[MDEG] = deg; tr->dp[DFXH] = h0;
+  sig2 = tr->dp[DRV]; 
+  printf("sd est: %8.5f\n",sqrt(tr->dp[DRV]));
+
+  for (i=0; i<*nmeth; i++)
+  { switch(meth[i])
+    { case 1: hhat[i] = cp(des,tr,1);
+              break;
+      case 2: hhat[i] = cp(des,tr,2);
+              break;
+      case 3: hhat[i] = gkk(des,tr);
+              break;
+      case 4: hhat[i] = rsw(des,tr,kk);
+              break;
+      default: hhat[i] = 0;
+    }
+    tr->dp[DFXH] = h0;
+    tr->mi[MDEG] = deg;
+  }
+}
diff --git a/src/locfit/solve.c b/src/locfit/solve.c
new file mode 100644
index 0000000..1ad3e64
--- /dev/null
+++ b/src/locfit/solve.c
@@ -0,0 +1,119 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *  solve f(x)=c by various methods, with varying stability etc...
+ *    xlo and xhi should be initial bounds for the solution.
+ *    convergence criterion is |f(x)-c| < tol.
+ *
+ *  double solve_secant(f,c,xlo,xhi,tol,bd_flag,err)
+ *    secant method solution of f(x)=c.
+ *    xlo and xhi are starting values and bound for solution.
+ *    tol = convergence criterion, |f(x)-c| < tol.
+ *    bd_flag = if (xlo,xhi) doesn't bound a solution, what action to take?
+ *      BDF_NONE returns error.
+ *      BDF_EXPRIGHT increases xhi.
+ *      BDF_EXPLEFT  decreases xlo.
+ *    err = error flag.
+ *    The (xlo,xhi) bound is not formally necessary for the secant method.
+ *    But having such a bound vastly improves stability; the code performs
+ *    a bisection step whenever the iterations run outside the bounds.
+ *
+ *  double solve_nr(f,f1,c,x0,tol,err)
+ *    Newton-Raphson solution of f(x)=c.
+ *    f1 = f'(x).
+ *    x0 = starting value.
+ *    tol = convergence criteria, |f(x)-c| < tol.
+ *    err = error flag.
+ *    No stability checks at present.
+ *
+ *  double solve_fp(f,x0,tol)
+ *    fixed-point iteration to solve f(x)=x.
+ *    x0 = starting value.
+ *    tol = convergence criteria, stops when |f(x)-x| < tol.
+ *    Convergence requires |f'(x)|<1 in neighborhood of true solution;
+ *      f'(x) \approx 0 gives the fastest convergence.
+ *    No stability checks at present.
+ *
+ *  TODO: additional error checking, non-convergence stop.
+ */
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "mutil.h"
+
+double solve_secant(f,c,xlo,xhi,tol,bd_flag,err)
+double (*f)(), c, xhi, xlo, tol;
+int bd_flag, *err;
+{ double ylo, yhi, x1, x2, x, y1, y2, y;
+  *err = 0;
+  ylo = f(xlo)-c;
+  yhi = f(xhi)-c;
+
+  switch(bd_flag)
+  { case BDF_EXPRIGHT:
+      while (yhi*ylo > 0)
+      { xhi += xhi-xlo;
+        yhi = f(xhi)-c;
+      }
+      break;
+    case BDF_EXPLEFT:
+      while (yhi*ylo > 0)
+      { xlo -= xhi-xlo;
+        ylo = f(xlo)-c;
+      }
+      break;
+    case BDF_NONE:
+    default:
+      if (yhi*ylo > 0)
+      { *err = 1;
+        return((xlo+xhi)/2);
+      }
+      break;
+  }
+
+  x1 = xlo; y1 = ylo;
+  x2 = xhi; y2 = yhi;
+
+  while (1)
+  { x = x2 + (x1-x2)*y2/(y2-y1);
+    if ((x<=xlo) | (x>=xhi)) x = (xlo+xhi)/2;
+    y = f(x)-c;
+    if (fabs(y) < tol) return(x);
+    if (y*ylo>0) { xlo = x; ylo = y; }
+            else { xhi = x; yhi = y; }
+if (y2==y)
+{ //printf("secant: y2 %12.9f\n",y2);
+  return(x);
+}
+    x1 = x2; y1 = y2;
+    x2 = x;  y2 = y;
+  }
+}
+
+double solve_nr(f,f1,c,x0,tol,err)
+double (*f)(), (*f1)(), c, x0, tol;
+int *err;
+{ double y;
+  do
+  { y = f(x0)-c;
+    x0 -= y/f1(x0);
+  } while (fabs(y)>tol);
+  return(x0);
+}
+
+double solve_fp(f,x0,tol,maxit)
+double (*f)(), x0, tol;
+int maxit;
+{ double x1;
+  int i;
+    x1 = 0;
+  for (i=0; i<maxit; i++)
+  { x1 = f(x0);
+    if (fabs(x1-x0)<tol) return(x1);
+    x0 = x1;
+  }
+  return(x1); /* although it hasn't converged */
+}
diff --git a/src/locfit/startlf.c b/src/locfit/startlf.c
new file mode 100644
index 0000000..6255410
--- /dev/null
+++ b/src/locfit/startlf.c
@@ -0,0 +1,447 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *
+ startlf(des,lf,vfun,nopc) -- starting point for locfit.
+ des and lf are pointers to the design and fit structures.
+ vfun is the vertex processing function.
+ nopc=1 inhibits computation of parametric component.
+ fitdefault(lf,n,d) -- fit default parameters.
+ lf is pointer to fit; n and d are sample size and dimension.
+ deschk()  -- assignment function for the design structure.
+ preproc() -- fit preprocessing (limits, scales, paramcomp etc.)
+ bbox()    -- compute bounding box.
+ 
+ fitoptions()
+ clocfit()  -- start point for CLocfit - interpret cmd line etc.
+ */
+
+#include "local.h"
+
+extern INT cvi;
+
+void fitdefault(lf,n,d)
+lfit *lf;
+INT n, d;
+{ INT i, *mi;
+    double *dp;
+    
+    dp = lf->dp;
+    mi = lf->mi;
+    
+    mi[MTG] = TNUL;
+    mi[MTG] = (lf->y==NULL) ? TDEN : (64+TGAUS);
+    mi[MLINK] = LDEFAU;
+    mi[MACRI] = ANONE;
+    mi[MDEG] = mi[MDEG0] = 2;
+    mi[MEV] = (ident==1) ? EDATA : ETREE;
+    mi[MKT] = KSPH; mi[MKER] = WTCUB;
+    mi[MIT] = IDEFA; mi[MDC] = mi[MREN] = 0;
+    mi[MK] = 100; mi[MMINT] = 20;
+    mi[MMXIT] = 20;
+    mi[MN] = n; mi[MDIM] = d;
+    mi[MDEB] = 0;
+    mi[MUBAS] = 0;
+    
+    
+    dp[DALP] = 0.7; dp[DFXH] = dp[DADP] = 0.0;
+    dp[DCUT] = 0.8;
+    
+    if (d<=0)
+        ERROR(("must set MDIM before calling fitdefault"));
+    for (i=0; i<d; i++)
+    { lf->sca[i] = 1.0;
+        lf->xl[i] = lf->xl[i+d] = 0.0;
+        lf->fl[i] = lf->fl[i+d] = 0.0;
+    }
+}
+
+int des_reqd(n,p)
+INT n, p;
+{ 
+    return (n*(p+5)+2*p*p+4*p + jac_reqd(p));
+}
+int des_reqi(INT n) { return(n); }
+
+void deschk(des,n,p)
+design *des;
+INT n, p;
+{ 
+    double *z;
+    des->dw = checkvarlen(des->dw,des_reqd(n,p),"_deswork",VDOUBLE);
+    z = vdptr(des->dw);
+    des->X = z; z += n*p;
+    setzero(des->X, n*p);
+    
+    des->w = z; z += n;
+    setzero(des->w, n);
+    
+    des->res=z; z += n;
+    setzero(des->res, n);
+    
+    des->di =z; z += n;
+    setzero(des->di, n);
+    
+    des->th =z; z += n;
+    setzero(des->th, n);
+    
+    des->wd =z; z += n;
+    setzero(des->wd, n);
+    
+    des->V  =z; z += p*p;
+    setzero(des->V, p*p);
+    
+    des->P  =z; z += p*p;
+    setzero(des->P, p*p);
+    
+    des->f1 =z; z += p;
+    setzero(des->f1, p);
+    
+    des->ss =z; z += p;
+    setzero(des->ss, p);
+    
+    des->oc =z; z += p;
+    setzero(des->oc, p);
+    
+    des->cf =z; z += p;
+    setzero(des->cf, p);
+    
+    z = jac_alloc(&des->xtwx,p,z);
+    
+    des->index = checkvarlen(des->index,des_reqi(n),"_desidx",VINT);
+    des->ind = (INT *)vdptr(des->index);
+    des->n = n; 
+    des->p = p;
+    des->xtwx.p = p;
+}
+
+void bbox(lf,bx)
+lfit *lf;
+double *bx;
+{ INT i, j, d, n;
+    double z, mx, mn;
+    d = lf->mi[MDIM]; n = lf->mi[MN];
+    for (i=0; i<d; i++)
+        if (bx[i]==bx[i+d])
+        { if (lf->sty[i]==STANGL)
+        { bx[i] = 0.0; bx[i+d] = 2*PI*lf->sca[i];
+        }
+        else
+        { mx = mn = datum(lf,i,0);
+            for (j=1; j<n; j++)
+            { mx = MAX(mx,datum(lf,i,j));
+                mn = MIN(mn,datum(lf,i,j));
+            }
+            if (lf->xl[i]<lf->xl[i+d]) /* user set xlim; maybe use them. */
+            { z = mx-mn;
+                if (mn-0.2*z < lf->xl[i]) mn = lf->xl[i];
+                if (mx+0.2*z > lf->xl[i+d]) mx = lf->xl[i+d];
+            }
+            bx[i] = mn;
+            bx[i+d] = mx;
+        }
+        }
+}
+
+void preproc(des,lf,nopc)
+design *des;
+lfit *lf;
+INT nopc;
+{ INT d, i, j, n;
+    double xb;
+    d = lf->mi[MDIM]; n = lf->mi[MN];
+    lf->mi[MLINK] = defaultlink(lf->mi[MLINK],lf->mi[MTG]);
+    if (!validlinks(lf->mi[MLINK],lf->mi[MTG]))
+    { ERROR(("Invalid family/link combination"));
+        return;
+    }
+    compparcomp(des,lf,nopc);
+    if (lf->w==NULL)
+        lf->dp[DSWT] = lf->mi[MN];
+    else
+    { lf->dp[DSWT] = 0;
+        for (i=0; i<lf->mi[MN]; i++) lf->dp[DSWT] += prwt(lf,i);
+    }
+    for (i=0; i<d; i++)
+        if (lf->sca[i]<=0) /* set automatic scales */
+        { if (lf->sty[i]==STANGL) lf->sca[i] = 1.0;
+        else
+        { xb = lf->sca[i] = 0.0;
+            for (j=0; j<n; j++) xb += datum(lf,i,j);
+            xb /= n;
+            for (j=0; j<n; j++) lf->sca[i] += SQR(datum(lf,i,j)-xb);
+            lf->sca[i] = sqrt(lf->sca[i]/(n-1));
+        }
+        }
+    bbox(lf,lf->fl);
+}
+
+#ifdef CVERSION
+extern void do_scbsim();
+#endif
+
+void startlf(des,lf,vfun,nopc)
+design *des;
+lfit *lf;
+INT (*vfun)(), nopc;
+{ 
+    INT i, *mi;
+    des->vfun = vfun;
+    mi = lf->mi;
+    mi[MP] = calcp(mi,mi[MDEG]);
+    des->pref = 0;
+    cvi = -1; /* inhibit cross validation */
+    deschk(des,mi[MN],mi[MP]);
+    if (mi[MDEB]>0) printf("preprocess\n");
+        preproc(des,lf,nopc);
+        if (mi[MDEB]>0) printf("preprocess ok\n");
+            if (lf_error) return;
+    lf->ord = 0;
+    makecfn(des,lf);
+    if ((mi[MDIM]==1) && (lf->sty[0]!=STANGL))
+    { i = 1;
+        while ((i<mi[MN]) && (datum(lf,0,i)>=datum(lf,0,i-1))) i++;
+        lf->ord = (i==mi[MN]);
+    }
+    
+    if (mi[MDEB]>0) printf("call eval structure\n");
+        switch(mi[MEV])
+    { case EPHULL: triang_start(des,lf); break;
+        case EDATA:  dataf(des,lf); break;
+        case ECROS:  crossf(des,lf); break;
+        case EGRID:  gridf(des,lf); break;
+        case ETREE:  atree_start(des,lf); break;
+        case EKDCE:  mi[MKT] = KCE;
+        case EKDTR:  kdtre_start(des,lf); break;
+        case EPRES:  preset(des,lf); break;
+        case EXBAR:  xbarf(des,lf); break;
+        case ENONE:  lf->nv = lf->nce = 0;
+            return;
+#ifdef CVERSION
+        case 100: do_scbsim(des,lf); break;
+#endif
+        default: ERROR(("startlf: Invalid evaluation structure"));
+    }
+    
+    /* renormalize for family=density */
+    if ((mi[MREN]) && (mi[MTG]==TDEN)) dens_renorm(lf,des);
+        }
+
+#ifdef CVERSION
+extern lfit lf;
+extern design des;
+extern plots pl[];
+int curwin;
+vari *vb;
+
+INT nofit()
+{ if (lf.mi==NULL) return(1);
+    return(lf.mi[MEV]==ENULL);
+}
+
+void endfit()
+{ INT i;
+    for (i=0; i<MAXWIN; i++)
+        if (pl[i].track != NULL)
+        { curwin = i;
+            cmdint(pl[i].track);
+        }
+}
+
+INT drl(key,dv,mi)
+char *key;
+INT *dv, *mi;
+{ INT i, nd;
+    nd = readilist(dv,key,0,mi[MDEG],0);
+    for (i=0; i<nd; i++)
+    { if ((dv[i]<1) | (dv[i]>mi[MDIM]))
+        ERROR(("drl: Invalid derivatives %s",key));
+        dv[i]--;
+    }
+    return(nd);
+}
+
+void fitoptions(lf,vc,re)
+lfit *lf;
+vari *vc;
+INT re;
+{ 
+    INT d = 0, n, i, i0, i1, *mi;
+    char kc, *key;
+    vari *v;
+    
+    re &= (!nofit());
+    i0 = getarg(vc,"formula",1);
+    if ((!re) && (i0==0)) { ERROR(("no formula")); return; }
+    i1 = getarg(vc,"data",1);
+    if (i1>0) doreaddata(argval(vc,i1),(INT)0);
+    if (re)
+        recondat(0,&lf->mi[MN]);
+    else
+    { lf->base = lf->y = lf->c = lf->w = NULL;
+        lf->nd = 0;
+        strcpy(lf->yname,"_NuLl");
+        strcpy(lf->wname,"_NuLl");
+        strcpy(lf->bname,"_NuLl");
+        strcpy(lf->cname,"_NuLl");
+    }
+    if (i0>0) /* interpret formula */
+    { key = argval(vc,i0);
+        n = -1;
+        i0 = i1 = 0; d = 0;
+        while ((i0<strlen(key)) && (key[i0]!='~')) i0++;
+        if (key[i0] != '~') { ERROR(("invalid formula %s",key)); return; }
+        if (i0>0)
+        { key[i0] = '\0';
+            lf->y = vdptr(findvar(key,1,&n));
+            strcpy(lf->yname,key);
+            key[i0] = '~';
+        }
+        i1 = i0 = i0+1;
+        while (i1<strlen(key))
+        { while ((i1<strlen(key)) && (key[i1]!='+')) i1++;
+            kc = key[i1]; key[i1] = '\0';
+            lf->sty[d] = KPROD;
+            if (stm(&key[i0],"left(",5))
+            { lf->sty[d] = STLEFT;
+                i0 = i0+5; key[i1-1] = '\0';
+            }
+            else if (stm(&key[i0],"right(",6))
+            { lf->sty[d] = STRIGH;
+                i0 = i0+6; key[i1-1] = '\0';
+            }
+            else if (stm(&key[i0],"ang(",4))
+            { lf->sty[d] = STANGL;
+                i0 = i0+4; key[i1-1] = '\0';
+            }
+            else if (stm(&key[i0],"cpar(",5))
+            { lf->sty[d] = STCPAR;
+                i0 = i0+5; key[i1-1] = '\0';
+            }
+            dvari(lf,d) = vdptr(findvar(&key[i0],1,&n));
+            strcpy(lf->xname[d],&key[i0]);
+            if (lf->sty[d]!=KPROD) key[i1-1] = ')';
+            d++; key[i1] = kc;
+            i0 = i1 = i1+1;
+        }
+        fitdefault(lf,n,d);
+    }
+    mi = lf->mi;
+    
+    i = getarg(vc,"weights",1);
+    if (i>0)
+    { lf->w = vdptr(findvar(argval(vc,i),1,&mi[MN]));
+        strcpy(lf->wname,argval(vc,i));
+    }
+    i = getarg(vc,"cens",1);
+    if (i>0)
+    { lf->c = vdptr(findvar(argval(vc,i),1,&mi[MN]));
+        strcpy(lf->cname,argval(vc,i));
+    }
+    i = getarg(vc,"base",1);
+    if (i>0)
+    { lf->base = vdptr(findvar(argval(vc,i),1,&mi[MN]));
+        strcpy(lf->bname,argval(vc,i));
+    }
+    
+    i = getarg(vc,"scale",1);
+    if (i>0)
+    { if (argvalis(vc,i,"T"))
+        for (i=0; i<d; i++) lf->sca[i] = 0;
+    else if (argvalis(vc,i,"F"))
+        for (i=0; i<d; i++) lf->sca[i] = 1;
+    else
+        arvect(argval(vc,i),lf->sca,d,0);
+    }
+    
+    i = getarg(vc,"vb",0);
+    if (i>0)
+    { lf->dp[DALP] = -1;
+        vb = arbuild(argval(vc,i),0,strlen(argval(vc,i))-1,NULL,0,1);
+        setvarname(vb,"_varband");
+    }
+    else
+    { i = getarg(vc,"alpha",1);
+        if (i>0) arvect(argval(vc,i),&lf->dp[DALP],3,1);
+    }
+    
+    i = getarg(vc,"deg",1);
+    if (i>0)
+    { i =  readilist(&mi[MDEG0],argval(vc,i),1,2,0);
+        if (i==1) mi[MDEG] = mi[MDEG0];
+    }
+    
+    i = getarg(vc,"family",1);if (i>0) setstrval(mi,MTG,argval(vc,i));
+    i = getarg(vc,"link",1);  if (i>0) setstrval(mi,MLINK,argval(vc,i));
+    i = getarg(vc,"ev",1); 
+    if (i>0)
+    { v = findvar(argval(vc,i),0,NULL);
+        if (v!=NULL)
+        { mi[MEV] = EPRES;
+            lf->xxev= v;
+            lf->nvm = v->n;
+        }
+        else
+            setstrval(mi,MEV,argval(vc,i));
+    }
+    i = getarg(vc,"acri",1);  if (i>0) setstrval(mi,MACRI,argval(vc,i));
+    
+    i = getarg(vc,"mg",1);
+    if (i>0) readilist(lf->mg,argval(vc,i),1,MXDIM,1);
+    
+    i = getarg(vc,"kt",1);   if (i>0) setstrval(mi,MKT, argval(vc,i));
+    i = getarg(vc,"kern",1); if (i>0) setstrval(mi,MKER,argval(vc,i));
+    i = getarg(vc,"itype",1);if (i>0) setstrval(mi,MIT, argval(vc,i));
+    
+    i = getarg(vc,"cut",1);
+    if (i>0) lf->dp[DCUT] = darith(argval(vc,i));
+    
+    i = getarg(vc,"flim",1);
+    if (i>0) arvect(argval(vc,i),lf->fl,2*d,2);
+    
+    i = getarg(vc,"xlim",1);
+    if (i>0) arvect(argval(vc,i),lf->xl,2*d,2);
+    
+    i = getarg(vc,"deriv",0);
+    if (i>0) lf->nd = drl(argval(vc,i),lf->deriv,lf->mi);
+    i = getarg(vc,"dc",1); if (i>0) mi[MDC] = getlogic(vc,i);
+    i = getarg(vc,"maxk",1); if (i>0) readilist(&mi[MK],argval(vc,i),1,1,0);
+    i = getarg(vc,"mint",1); if (i>0) readilist(&mi[MMINT],argval(vc,i),1,1,0);
+    i = getarg(vc,"maxit",1); if (i>0) readilist(&mi[MMXIT],argval(vc,i),1,1,0);
+    i = getarg(vc,"renorm",1);if (i>0) mi[MREN] = getlogic(vc,i);
+    i = getarg(vc,"debug",1); if (i>0) readilist(&mi[MDEB],argval(vc,i),1,1,0);
+}
+
+void clocfit(v,re)
+INT re;
+vari *v;
+{
+    lf.ord = 0;
+    lf.kap[0] = lf.kap[1] = lf.kap[2] = 0.0; lf.nk = 0;
+    fitoptions(&lf,v,re);
+    if (lf_error)
+    { if (lf.mi!=NULL) lf.mi[MEV] = ENULL;
+        return;
+    }
+    
+    
+    lf.nv = 0;
+    if (lf.mi[MDEG0]==lf.mi[MDEG])
+    { startlf(&des,&lf,procv,0);
+        if (!lf_error) ressumm(&lf,&des);
+    }
+    else
+        startlf(&des,&lf,procvvord,0);
+    if (lf_error)
+    { if (!re) lf.mi[MEV] = ENULL;
+        return;
+    }
+    
+    //printf("Evaluation structure %d, %d points.\n",lf.mi[MEV],lf.nv);
+    if (argarg(v,0) != NULL) dosavefit(&lf,argarg(v,0),"wb",(INT)0);
+    endfit();
+}
+
+#endif
diff --git a/src/locfit/strings.c b/src/locfit/strings.c
new file mode 100644
index 0000000..ce3dd32
--- /dev/null
+++ b/src/locfit/strings.c
@@ -0,0 +1,140 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *
+ *  miscellaneous string handling functions.
+ *  used mostly in arith (C version) and lfstr.c
+ *
+ *  stm(u,v,k)        do the first k components of u, v match?
+ *  ct_match(z1,z2)   counts number of matching components.
+ *  pmatch(z,strings,vals,n,def)
+ *                    finds the best match for z among the strings;
+ *                    returns corresponding component of vals.
+ *                    n = no of strings; def = value if no match.
+ *  matchrt(z,i,i2,op,cl)
+ *  matchlf(z,i1,i,op,cl)
+ *                    Parenthesis matching. If op='(' and cl=')', matchrt
+ *                    searches z, starting at z[i]='(' and ending and z[i2],
+ *                    for the closing ')', taking care of nesting.
+ *                    matchlf does the reverse.
+ *
+ *  checkltor(z,i1,i2,c)
+ *                    Checks the string z, left to right from z[i1] to z[i2]
+ *                    but skipping parenthesized () and [] parts, for the
+ *                    occurence of any character from c. If a match is found,
+ *                    the index is returned. If no match is found, return -1.
+ *
+ *  checkrtol(z,i1,i2,c) Same as checkltor(), but searches right to left.
+ *  strip(z)          replaces underscores in z by spaces.
+ */
+
+#include "local.h"
+
+/* do the first k components of u, v match? */
+int stm(char *u, char *v, int k) { return((strncmp(u,v,k)==0)); }
+
+int ct_match(z1, z2)
+char *z1, *z2;
+{ int ct = 0;
+  while (z1[ct]==z2[ct])
+  { if (z1[ct]=='\0') return(ct+1);
+    ct++;
+  }
+  return(ct);
+}
+
+int pmatch(z, strings, vals, n, def)
+char *z, **strings;
+int *vals, n, def;
+{ int i, ct, best, best_ct;
+  best = -1;
+  best_ct = 0;
+
+  for (i=0; i<n; i++)
+  { ct = ct_match(z,strings[i]);
+    if (ct==strlen(z)+1) return(vals[i]);
+    if (ct>best_ct) { best = i; best_ct = ct; }
+  }
+  if (best==-1) return(def);
+  return(vals[best]);
+}
+
+int matchrt(z,i,i2,op,cl)
+char *z, op, cl;
+int i, i2;
+{ int k;
+  if (z[i] != op)
+  { ERROR(("matchrt: wrong start character"));
+    return(i);
+  }
+  k = 0;
+  while (1)
+  { if (z[i]==op) k++;
+    if (z[i]==cl) k--;
+    if (k==0) return(i);
+    i++;
+    if (i>i2)
+    { ERROR(("matchrt: unbalanced %c%c: %s",op,cl,z));
+      return(i);
+    }
+  }
+}
+
+int matchlf(z,i1,i,op,cl)
+char *z, op, cl;
+int i, i1;
+{ int k;
+  if (z[i] != cl)
+  { ERROR(("matchlf: wrong end character"));
+    return(i);
+  }
+  k = 0;
+  while (1)
+  { if (z[i]==op) k--;
+    if (z[i]==cl) k++;
+    if (k==0) return(i);
+    i--;
+    if (i<i1)
+    { ERROR(("matchlf: unbalanced %c%c: %s",op,cl,z));
+      return(i);
+    }
+  }
+}
+
+int checkltor(z,i1,i2,c)
+char *z, *c;
+int i1, i2;
+{ int i;
+  i = i1;
+  while (i<=i2)
+  {
+    if (strchr(c,z[i]) != NULL) return(i);
+    if (z[i]=='(') i = matchrt(z,i,i2,'(',')');
+    if (z[i]=='[') i = matchrt(z,i,i2,'[',']');
+    i++;
+    if (lf_error) return(-1);
+  }
+  return(-1);
+}
+
+int checkrtol(z,i1,i2,c)
+char *z, *c;
+int i1, i2;
+{ int i;
+  i = i2;
+  while (i >= i1)
+  { if (strchr(c,z[i]) != NULL) return(i);
+    if (z[i]==')') i = matchlf(z,i1,i,'(',')');
+    if (z[i]==']') i = matchlf(z,i1,i,'[',']');
+    i--;
+    if (lf_error) return(-1);
+  }
+  return(-1);
+}
+
+void strip(z)
+char *z;
+{ do { if (*z=='_') *z=' '; } while (*(++z)!='\0');
+}
diff --git a/src/locfit/vari.cpp b/src/locfit/vari.cpp
new file mode 100644
index 0000000..eabc450
--- /dev/null
+++ b/src/locfit/vari.cpp
@@ -0,0 +1,299 @@
+/*
+ *   Copyright (c) 1996-2000 Lucent Technologies.
+ *   See README file for details.
+ *
+ *  Functions for handling locfit variables in the C version.
+ */
+
+extern "C" 
+{
+#include "local.h"
+}
+
+#include <errno.h>
+
+#include "vari.hpp"
+//#define MAXV 1000
+//#define LF_WORK 102400
+
+//static char *db = NULL;
+//static INT lfwptr, lf_work;
+//vari root;
+
+
+
+#include <map>
+#include <string>
+
+using namespace std;
+
+
+typedef map<string, vari*> VarTable;
+VarTable var_table;
+
+void cleardb()
+{
+    for (VarTable::iterator i = var_table.begin(); i != var_table.end(); ++i)
+    {
+        if (i->second && i->second->stat != STSYSPEC)
+        {
+            free(i->second->dpr);
+        }
+        free(i->second);
+    }
+    var_table.clear();
+}
+
+void initdb() /* initialize locfit's work space */
+{ 
+    cleardb();
+    
+//    char *z = NULL;
+//    z = getenv("LFWORK");
+//    if (z==NULL) lf_work = LF_WORK;
+//    else sscanf(z,"%d",&lf_work);
+//    lf_work <<= 10;
+//    if (db != NULL)
+//    {
+//        free(db);
+//        lfwptr = 0;
+//    }
+//    db = (char *)calloc(lf_work, 1);
+//    if (db == NULL)
+//    {
+//        fprintf(stderr, "Error: Locfit working space could not be allocated!\n");
+//        fprintf(stderr, "Error code %d\n", errno);
+//    }
+//    
+//    root.stat = STSYSTEM;
+//    root.mode = VVARI;
+//    root.dpr = (double *)db;
+//    lfwptr = root.bytes = MAXV*sizeof(vari);
+//    root.n = 0;
+}
+
+
+INT vbytes(int n, int mode)
+{ 
+    switch(mode)
+    { 
+        case VDOUBLE: return(n*sizeof(double));
+        case VINT:    return(n*sizeof(INT));
+        case VCHAR:   return(n);
+        case VARGL:   return(n*sizeof(carg));
+        case VPREP:   return(sizeof(pplot));
+        case VARC:    return(n*sizeof(arstruct));
+        case VVARI:   return(n*sizeof(vari));
+        case VXYZ:    return(n*sizeof(plxyz));
+    }
+    ERROR(("unknown mode %d in vbytes",mode));
+    return(0);
+}
+
+/* vdptr with NULL check */
+double *vdptr(vari* v)
+
+{
+    if (v==NULL) 
+        return(NULL);
+    return(v->dpr);
+}
+
+/* return the i'th data item. Cyclic. */
+double vitem(vari* v, int i)
+{ 
+    int index;
+    if ((v==NULL) || (vlength(v)==0)) 
+        return(0.0);
+    index = i % vlength(v);
+    switch(v->mode)
+    { case VDOUBLE: return( vdptr(v)[index] );
+        case VINT:
+        { INT *z;
+            z = (INT *)vdptr(v);
+            return(z[index]);
+        }
+        case VCHAR:
+        { char *z;
+            z = (char *)vdptr(v);
+            return(z[index]);
+        }
+    }
+    ERROR(("Invalid mode in vitem()"));
+    return(0.0);
+}
+
+void vassn(vari* v, int i, double x)
+{ 
+    vdptr(v)[i] = x;
+}
+
+vari *growvar(vari* vold, int n)
+{ 
+    fprintf(stderr, "Error: attempting to grow variable not supported\n");
+    return NULL;
+//    vari *vnew;
+//    int reqd_bytes;
+//    
+//    if (vold==NULL)
+//    { 
+//        ERROR(("growvar: NULL old"));
+//        return(NULL);
+//    }
+//    
+//    reqd_bytes = vbytes(n, vmode(vold));
+//    if (reqd_bytes <= vold->bytes) 
+//        return(vold);
+//    
+//    vnew = createvar("_grow",vold->stat,n,vmode(vold));
+//    memcpy(vdptr(vnew),vdptr(vold),vbytes(vlength(vold),vmode(vold)));
+//    setvarname(vnew,vold->name);
+//    vlength(vnew) = vlength(vold);
+//    deletevar(vold);
+//    return(vnew);
+}
+
+void *viptr(vari* v, int i) /* return pointer to ith data item, take account of mode */
+{ switch(vmode(v))
+    { case VDOUBLE: return(&v->dpr[i]);
+        case VCHAR: return(&((char *)v->dpr)[i]);
+        case VARGL: return(&((carg *)v->dpr)[i]);
+        case VARC:  return(&((arstruct *)v->dpr)[i]);
+        case VVARI: return(&((vari *)v->dpr)[i]);
+        case VXYZ:  return(&((plxyz *)v->dpr)[i]);
+    }
+    ERROR(("Unknown mode %d in viptr",vmode(v)));
+    return(NULL);
+}
+
+void setvarname(vari* v, varname name)
+{ 
+    if (strcmp(v->name,name)==0) 
+        return;
+    deletename(name);
+    strcpy(v->name,name);
+}
+
+/*
+ findvar finds the variable name.
+ err=0, keep quiet if not found; 1 produce error message.
+ *n returns length of variable (if initially>0, checks length)
+ */
+
+vari *findvar(varname name, int err, int* n)
+{ 
+    INT status;
+    vari *v;
+    
+    if (strcmp(name,"_NuLl")==0) return(NULL);
+    
+    VarTable::iterator i = var_table.find(name);
+    
+    if (i != var_table.end())
+    {
+        v = i->second;
+        if (v == NULL)
+        {
+            fprintf(stderr, "Found variable named %s, but data is NULL\n", name);
+            return NULL;
+        }
+        status = v->stat;
+        if (status != STHIDDEN && status != STEMPTY)
+        {
+            if (n == NULL)
+                return v;
+            if (*n==-1) 
+                *n = vlength(v);
+            if (*n==0 || *n==vlength(v)) 
+                return(v);
+            if (err) 
+                ERROR(("Variable %s has wrong length",name));
+        }
+    }
+
+    if (err) 
+        ERROR(("Variable %s not found",name));
+    return NULL;
+}
+
+void deletevar(vari* v) /* delete variable, or top variable if NULL */
+{ 
+    if (v == NULL)
+    {
+        fprintf(stderr, "Error: attempting to clear entire table through NULL delete\n");
+        return;
+    }
+    
+    VarTable::iterator i = var_table.find(v->name);
+    if (i != var_table.end())
+    {   
+        if (i->second && i->second->stat != STSYSPEC)
+        {
+            free(i->second->dpr);
+        }
+        free(i->second);
+        var_table.erase(i);
+    }
+}
+
+void deleteifhidden(vari* v)
+{ 
+    if (v==NULL) 
+        return;
+    if (v->stat == STHIDDEN) deletevar(v);
+}
+
+void deletename(varname name) /* delete variable name, or top variable if NULL */
+{ 
+    vari *v;
+    v = findvar(name,0,NULL);
+    if (v!=NULL) 
+        deletevar(v);
+}
+
+vari *createvar(varname name, int status, int n, int mode)
+{
+    int bytes;
+    vari *v;
+    
+    /*
+     compute the length of the variable in bytes. some systems
+     mess up is this is not a multiple of 8.
+     */
+    bytes = vbytes(n,mode);
+    while ( (bytes & 8) > 0 ) bytes++;
+    
+    if (lf_error) 
+        return(NULL);
+    
+    // Don't delete the hidden vars
+    if (status==STSYSTEM || status==STREGULAR || status==STPLOTVAR)
+        deletename(name);
+    
+    v = findvar(name,0,NULL);
+    if (v != NULL)
+    {
+        fprintf(stderr, "Error: attempting to re-initialize still-live variable %s\n", name);
+    }
+    
+    pair<map<string, vari*>::iterator, bool> inserted;
+    string str_name = name;
+    pair<string, vari*> p;
+    p.first = str_name;
+    p.second = (vari*)calloc(1, sizeof(vari));
+    inserted = var_table.insert(p);
+    
+    v = inserted.first->second;
+    
+    strcpy(v->name,name);
+    vlength(v) = n;
+    v->stat = status;
+    v->bytes = bytes;
+    v->mode = mode;
+    if (status!=STSYSPEC)
+    { 
+        v->dpr = (double*)calloc(bytes, 1);
+    }
+
+    return(v);
+}
diff --git a/src/locfit/vari.hpp b/src/locfit/vari.hpp
new file mode 100644
index 0000000..bc3ce96
--- /dev/null
+++ b/src/locfit/vari.hpp
@@ -0,0 +1,33 @@
+//
+//  vari.hpp
+//  cufflinks
+//
+//  Created by Cole Trapnell on 3/22/11.
+//  Copyright 2011 Cole Trapnell. All rights reserved.
+//
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+    void cleardb();
+    void initdb();
+    INT vbytes(int n, int mode);
+    void setvarname(vari* v, varname name);
+    double *vdptr(vari* v);
+    double vitem(vari* v, int i);
+    void vassn(vari* v, int i, double x);
+    vari *findvar(varname name, int err, int* n);
+    vari *growvar(vari* vold, int n);
+    void *viptr(vari* v, int i);
+    void setvarname(vari* v, varname name);
+    vari *findvar(varname name, int err, int* n);
+    
+    void deletevar(vari* v); /* delete variable, or top variable if NULL */
+    void deleteifhidden(vari* v);
+    vari *createvar(varname name, int status, int n, int mode);
+    void deletename(varname name);
+    
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/locfit/wdiag.c b/src/locfit/wdiag.c
new file mode 100644
index 0000000..8cb241d
--- /dev/null
+++ b/src/locfit/wdiag.c
@@ -0,0 +1,260 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+  Routines for computing weight diagrams.
+  wdiag(lf,des,lx,deg,ty,exp)
+  Must locfit() first, unless ker==WPARM and has par. comp.
+ 
+  also, vertex processing function procvhatm().
+
+  cwdiag() entry for CLOCFIT.
+ */
+
+#include "local.h"
+
+static double *wd;
+extern double robscale;
+extern void unitvec();
+
+void nnresproj(lf,des,u,m,p,mi)
+lfit *lf;
+design *des;
+double *u;
+INT m, p, *mi;
+{ INT i, j;
+  double link[LLEN];
+  setzero(des->f1,p);
+  for (j=0; j<m; j++)
+  { stdlinks(link,lf,des->ind[j],des->th[j],robscale);
+    for (i=0; i<p; i++) des->f1[i] += link[ZDDLL]*d_xij(des,j,i)*u[j];
+  }
+  jacob_solve(&des->xtwx,des->f1);
+  for (i=0; i<m; i++)
+    u[i] -= innerprod(des->f1,d_xi(des,i),p)*des->w[i];
+}
+
+void wdexpand(l,n,ind,m)
+double *l;
+INT n, m, *ind;
+{ INT i, j, t;
+  double z;
+  for (j=m; j<n; j++) { l[j] = 0.0; ind[j] = -1; }
+  j = m-1;
+  while (j>=0)
+  { if (ind[j]==j) j--;
+    else
+    { i = ind[j];
+      z = l[j]; l[j] = l[i]; l[i] = z;
+      t = ind[j]; ind[j] = ind[i]; ind[i] = t;
+      if (ind[j]==-1) j--;
+    }
+  }
+
+/*  for (i=n-1; i>=0; i--)
+  { l[i] = ((j>=0) && (ind[j]==i)) ? l[j--] : 0.0; } */
+}
+
+INT wdiagp(lf,des,lx,deg,ty,exp)
+lfit *lf;
+design *des;
+double *lx;
+INT deg, ty, exp;
+{ INT i, j, p, *mi, *deriv, nd;
+  double *l1;
+  mi = lf->mi; p = des->p;
+  deriv = lf->deriv; nd = lf->nd;
+  fitfun(lf,des->xev,lf->pc.xbar,des->f1,deriv,nd);
+  if (exp)
+  { jacob_solve(&lf->pc.xtwx,des->f1);
+    for (i=0; i<mi[MN]; i++)
+      lx[i] = innerprod(des->f1,d_xi(des,i),p);
+    return(mi[MN]);
+  }
+  jacob_hsolve(&lf->pc.xtwx,des->f1);
+  for (i=0; i<p; i++) lx[i] = des->f1[i];
+
+  if (deg>=1)
+    for (i=0; i<mi[MDIM]; i++)
+    { deriv[nd] = i;
+      l1 = &lx[(i+1)*p];
+      fitfun(lf,des->xev,lf->pc.xbar,l1,deriv,nd+1);
+      jacob_hsolve(&lf->pc.xtwx,l1);
+    }
+
+  if (deg>=2)
+    for (i=0; i<mi[MDIM]; i++)
+    { deriv[nd] = i;
+      for (j=0; j<mi[MDIM]; j++)
+      { deriv[nd+1] = j;
+        l1 = &lx[(i*mi[MDIM]+j+mi[MDIM]+1)*p];
+        fitfun(lf,des->xev,lf->pc.xbar,l1,deriv,nd+2);
+        jacob_hsolve(&lf->pc.xtwx,l1);
+    } }
+  return(p);
+}
+
+INT wdiag(lf,des,lx,deg,ty,exp)
+lfit *lf;
+design *des;
+double *lx;
+INT deg, ty, exp;
+/* deg=0: l(x) only.
+   deg=1: l(x), l'(x) (approx/exact ? mi[MDC] );
+   deg=2: l(x), l'(x), l''(x);
+   ty = 1: e1 (X^T WVX)^{-1} X^T W        -- hat matrix
+   ty = 2: e1 (X^T WVX)^{-1} X^T WV^{1/2} -- scb's
+*/
+{ double w, *X, *lxd, *lxdd, wdd, wdw, *ulx, link[LLEN], h;
+  double dfx[MXDIM], hs[MXDIM];
+  INT i, ii, j, k, l, m, d, p, *mi, *deriv, nd;
+  if ((lf->mi[MKER]==WPARM) && (hasparcomp(lf)))
+    return(wdiagp(lf,des,lx,deg,ty,exp));
+  mi = lf->mi; h = des->h;
+  deriv = lf->deriv; nd = lf->nd;
+  wd = des->wd;
+  d = mi[MDIM]; p = des->p; X = d_x(des);
+  ulx = des->res;
+  m = des->n;
+  for (i=0; i<d; i++) hs[i] = h*lf->sca[i];
+  if (deg>0)
+  { lxd = &lx[m];
+    setzero(lxd,m*d);
+    if (deg>1)
+    { lxdd = &lxd[d*m];
+      setzero(lxdd,m*d*d);
+  } }
+  if (nd>0) fitfun(lf,des->xev,des->xev,des->f1,deriv,nd); /* c(0) */
+    else unitvec(des->f1,0,p);
+  jacob_solve(&des->xtwx,des->f1);   /* c(0) (X^TWX)^{-1} */
+  for (i=0; i<m; i++)
+  { ii = des->ind[i];
+    lx[i] = innerprod(des->f1,&X[i*p],p); /* c(0)(XTWX)^{-1}X^T */
+    if ((deg>0) && (mi[MDC]))
+    { wd[i] = Wd(des->di[ii]/h,mi[MKER]);
+      for (j=0; j<d; j++)
+      { dfx[j] = datum(lf,j,ii)-des->xev[j];
+        lxd[j*m+i] = lx[i]*des->w[i]*weightd(dfx[j],lf->sca[j],
+          d,mi[MKER],mi[MKT],h,lf->sty[j],des->di[ii]);
+             /* c(0) (XTWX)^{-1}XTW' */
+      }
+      if (deg>1)
+      { wdd = Wdd(des->di[ii]/h,mi[MKER]);
+        for (j=0; j<d; j++)
+          for (k=0; k<d; k++)
+          { w = (des->di[ii]==0) ? 0 : h/des->di[ii];
+            w = wdd * (des->xev[k]-datum(lf,k,ii)) * (des->xev[j]-datum(lf,j,ii))
+                  * w*w / (hs[k]*hs[k]*hs[j]*hs[j]);
+            if (j==k) w += wd[i]/(hs[j]*hs[j]);
+            lxdd[(j*d+k)*m+i] = lx[i]*w;
+              /* c(0)(XTWX)^{-1}XTW'' */
+          }
+      }
+    }
+    lx[i] *= des->w[i];
+  }
+  if ((deg==2) && (mi[MDC]))
+  { for (i=0; i<d; i++)
+    { deriv[nd] = i;
+      fitfun(lf,des->xev,des->xev,des->f1,deriv,nd+1);
+      for (k=0; k<m; k++)
+      { stdlinks(link,lf,des->ind[k],des->th[k],robscale);
+        for (j=0; j<p; j++)
+          des->f1[j] -= link[ZDDLL]*lxd[i*m+k]*X[k*p+j];
+        /* c'(x)-c(x)(XTWX)^{-1}XTW'X */
+      }
+      jacob_solve(&des->xtwx,des->f1); /* (...)(XTWX)^{-1} */
+      for (j=0; j<m; j++)
+        ulx[j] = innerprod(des->f1,&X[j*p],p); /* (...)XT */
+      for (j=0; j<d; j++)
+        for (k=0; k<m; k++)
+        { ii = des->ind[k];
+          dfx[j] = datum(lf,j,ii)-des->xev[j];
+          wdw = des->w[k]*weightd(dfx[j],lf->sca[j],d,mi[MKER],mi[MKT],h,
+            lf->sty[j],des->di[ii]);
+          lxdd[(i*d+j)*m+k] += ulx[k]*wdw;
+          lxdd[(j*d+i)*m+k] += ulx[k]*wdw;
+        }
+        /* + 2(c'-c(XTWX)^{-1}XTW'X)(XTWX)^{-1}XTW' */
+    }
+    for (j=0; j<d*d; j++) nnresproj(lf,des,&lxdd[j*m],m,p,mi);
+        /* * (I-X(XTWX)^{-1} XTW */
+  }
+  if (deg>0)
+  { if (mi[MDC]) for (j=0; j<d; j++) nnresproj(lf,des,&lxd[j*m],m,p,mi);
+             /* c(0)(XTWX)^{-1}XTW'(I-X(XTWX)^{-1}XTW) */
+    for (i=0; i<d; i++)
+    { deriv[nd]=i;
+      fitfun(lf,des->xev,des->xev,des->f1,deriv,nd+1);
+      jacob_solve(&des->xtwx,des->f1);
+      for (k=0; k<m; k++)
+        for (l=0; l<p; l++)
+          lxd[i*m+k] += des->f1[l]*X[k*p+l]*des->w[k];
+            /* add c'(0)(XTWX)^{-1}XTW */
+    }
+  }
+  if (deg==2)
+  { for (i=0; i<d; i++)
+    { deriv[nd]=i;
+      for (j=0; j<d; j++)
+      { deriv[nd+1]=j;
+        fitfun(lf,des->xev,des->xev,des->f1,deriv,nd+2);
+        jacob_solve(&des->xtwx,des->f1);
+        for (k=0; k<m; k++)
+          for (l=0; l<p; l++)
+            lxdd[(i*d+j)*m+k] += des->f1[l]*X[k*p+l]*des->w[k];
+        /* + c''(x)(XTWX)^{-1}XTW */
+      }
+    }
+  }
+  k = 1+d*(deg>0)+d*d*(deg==2);
+
+  if (exp) wdexpand(lx,mi[MN],des->ind,m);
+ 
+  if (ty==1) return(m);
+  for (i=0; i<m; i++)
+  { stdlinks(link,lf,des->ind[i],des->th[i],robscale);
+    link[ZDDLL] = sqrt(fabs(link[ZDDLL]));
+    for (j=0; j<k; j++) lx[j*m+i] *= link[ZDDLL];
+  }
+  return(m);
+}
+
+INT procvhatm(des,lf,v)
+design *des;
+lfit *lf;
+INT v;
+{ INT k = 0, n;
+  double *l;
+  n = (ident==0) ? lf->mi[MN] : des->p;
+  if ((lf->mi[MKER]!=WPARM) | (!hasparcomp(lf))) k = procvraw(des,lf,v);
+  l = (double *)viptr(lf->L,v*n);
+  wdiag(lf,des,l,(INT)0,(INT)1,1);
+  return(k);
+}
+
+#ifdef CVERSION
+extern lfit lf;
+extern design des;
+
+void cwdiag(v)
+vari *v;
+{ INT i;
+  vari *ve, *vr;
+  i = getarg(v,"ev",0);
+  if (i==0) ERROR(("wdiag: no ev point"));
+  fitoptions(&lf,v,0);
+  if (lf_error) return;
+  ve = varith(argval(v,i),"wdev",STPLOTVAR);
+  vr = createvar("wdres",STHIDDEN,lf.mi[MN],VDOUBLE);
+  lf.xxev = ve;
+  lf.nv = lf.nvm = 1;
+  lf.L = vr;
+  lf.mi[MEV] = EPRES;
+  startlf(&des,&lf,procvhatm,1);
+  deletevar(ve);
+  saveresult(vr,argarg(v,0),STREGULAR);
+}
+
+#endif
diff --git a/src/locfit/weight.c b/src/locfit/weight.c
new file mode 100644
index 0000000..1e1e0ef
--- /dev/null
+++ b/src/locfit/weight.c
@@ -0,0 +1,462 @@
+/*
+ *   Copyright (c) 1996-2001 Lucent Technologies.
+ *   See README file for details.
+ *
+ *
+ *  Defines the weight functions and related quantities used
+ *  in LOCFIT.
+ */
+
+#include "local.h"
+
+
+/* The weight functions themselves.  Used everywhere. */
+double W(u,ker)
+double u;
+INT ker;
+{ u = fabs(u);
+  switch(ker)
+  { case WRECT: return((u>1) ? 0.0 : 1.0);
+    case WEPAN: return((u>1) ? 0.0 : 1-u*u);
+    case WBISQ: if (u>1) return(0.0);
+                u = 1-u*u; return(u*u);
+    case WTCUB: if (u>1) return(0.0);
+                u = 1-u*u*u; return(u*u*u);
+    case WTRWT: if (u>1) return(0.0);
+                u = 1-u*u; return(u*u*u);
+    case WQUQU: if (u>1) return(0.0);
+                u = 1-u*u; return(u*u*u*u);
+    case WTRIA: if (u>1) return(0.0);
+                return(1-u);
+    case W6CUB: if (u>1) return(0.0);
+                u = 1-u*u*u; u = u*u*u; return(u*u);
+    case WGAUS: return(exp(-SQR(GFACT*u)/2.0));
+    case WEXPL: return(exp(-EFACT*u));
+    case WMACL: return(1/((u+1.0e-100)*(u+1.0e-100)));
+    case WMINM: ERROR(("WMINM in W"));
+                return(0.0);
+    case WPARM: return(1.0);
+  }
+  return(0.0);
+}
+
+INT iscompact(ker)
+INT ker;
+{ if ((ker==WEXPL) | (ker==WGAUS) | (ker==WMACL) | (ker==WPARM)) return(0);
+  return(1);
+}
+
+double weightprod(lf,u,h)
+lfit *lf;
+double *u, h;
+{ INT i, ker;
+  double sc, w;
+  w = 1.0;
+  ker = lf->mi[MKER];
+  for (i=0; i<lf->mi[MDIM]; i++)
+  { sc = lf->sca[i];
+    switch(lf->sty[i])
+    { case STLEFT:
+        if (u[i]>0) return(0.0);
+        w *= W(-u[i]/(h*sc),ker);
+        break;
+      case STRIGH:
+        if (u[i]<0) return(0.0);
+        w *= W(u[i]/(h*sc),ker);
+        break;
+      case STANGL:
+        w *= W(2*fabs(sin(u[i]/(2*sc)))/h,ker);
+        break;
+      case STCPAR:
+        break;
+      default:
+        w *= W(fabs(u[i])/(h*sc),ker);
+    }
+    if (w==0.0) return(w);
+  }
+  return(w);
+}
+
+double weightsph(lf,u,h,hasdi,di)
+lfit *lf;
+double *u, h, di;
+INT hasdi;
+{ INT i;
+
+  if (!hasdi) di = rho(u,lf->sca,lf->mi[MDIM],lf->mi[MKT],lf->sty);
+
+  for (i=0; i<lf->mi[MDIM]; i++)
+  { if ((lf->sty[i]==STLEFT) && (u[i]>0.0)) return(0.0);
+    if ((lf->sty[i]==STRIGH) && (u[i]<0.0)) return(0.0);
+  }
+  if (h==0) return((di==0.0) ? 1.0 : 0.0);
+
+  return(W(di/h,lf->mi[MKER]));
+}
+
+double weight(lf,x,t,h,hasdi,di)
+lfit *lf;
+double *x, *t, h, di;
+INT hasdi;
+{ double u[MXDIM];
+  INT i;
+  for (i=0; i<lf->mi[MDIM]; i++) u[i] = (t==NULL) ? x[i] : x[i]-t[i];
+  switch(lf->mi[MKT])
+  { case KPROD: return(weightprod(lf,u,h));
+    case KSPH:  return(weightsph(lf,u,h,hasdi,di));
+  }
+  ERROR(("weight: unknown kernel type %d",lf->mi[MKT]));
+  return(1.0);
+}
+
+double sgn(x)
+double x;
+{ if (x>0) return(1.0);
+  if (x<0) return(-1.0);
+  return(0.0);
+}
+
+double WdW(u,ker) /* W'(u)/W(u) */
+double u;
+INT ker;
+{ double eps=1.0e-10;
+  if (ker==WGAUS) return(-GFACT*GFACT*u);
+  if (ker==WPARM) return(0.0);
+  if (fabs(u)>=1) return(0.0);
+  switch(ker)
+  { case WRECT: return(0.0);
+    case WTRIA: return(-sgn(u)/(1-fabs(u)+eps));
+    case WEPAN: return(-2*u/(1-u*u+eps));
+    case WBISQ: return(-4*u/(1-u*u+eps));
+    case WTRWT: return(-6*u/(1-u*u+eps));
+    case WTCUB: return(-9*sgn(u)*u*u/(1-u*u*fabs(u)+eps));
+    case WEXPL: return((u>0) ? -EFACT : EFACT);
+  }
+  ERROR(("WdW: invalid kernel"));
+  return(0.0);
+}
+
+/* deriv. weights .. spherical, product etc
+   u, sc, sty needed only in relevant direction
+   Acutally, returns (d/dx W(||x||/h) ) / W(.)
+*/
+double weightd(u,sc,d,ker,kt,h,sty,di)
+double u, sc, h, di;
+INT d, ker, kt, sty;
+{ if (sty==STANGL)
+  { if (kt==KPROD)
+      return(-WdW(2*sin(u/(2*sc)),ker)*cos(u/(2*sc))/(h*sc));
+    if (di==0.0) return(0.0);
+    return(-WdW(di/h,ker)*sin(u/sc)/(h*sc*di));
+  }
+  if (sty==STCPAR) return(0.0);
+  if (kt==KPROD)
+    return(-WdW(u/(h*sc),ker)/(h*sc));
+  if (di==0.0) return(0.0);
+  return(-WdW(di/h,ker)*u/(h*di*sc*sc));
+}
+
+double weightdd(u,sc,d,ker,kt,h,sty,di,i0,i1)
+double *u, *sc, h, di;
+INT d, ker, kt, *sty, i0, i1;
+{ double w;
+  w = 1;
+  if (kt==KPROD)
+  {
+    w = WdW(u[i0]/(h*sc[i0]),ker)*WdW(u[i1]/(h*sc[i1]),ker)/(h*h*sc[i0]*sc[i1]);
+  }
+  return(0.0);
+}
+
+/* Derivatives W'(u)/u.
+   Used in simult. conf. band computations,
+   and kernel density bandwidth selectors. */
+double Wd(u,ker)
+double u;
+INT ker;
+{ double v;
+  if (ker==WGAUS) return(-SQR(GFACT)*exp(-SQR(GFACT*u)/2));
+  if (ker==WPARM) return(0.0);
+  if (fabs(u)>1) return(0.0);
+  switch(ker)
+  { case WEPAN: return(-2.0);
+    case WBISQ: return(-4*(1-u*u));
+    case WTCUB: v = 1-u*u*u;
+                return(-9*v*v*u);
+    case WTRWT: v = 1-u*u;
+                return(-6*v*v);
+    default: ERROR(("Invalid kernel %d in Wd",ker));
+  }
+  return(0.0);
+}
+
+/* Second derivatives W''(u)-W'(u)/u.
+   used in simult. conf. band computations in >1 dimension. */
+double Wdd(u,ker)
+double u;
+INT ker;
+{ double v;
+  if (ker==WGAUS) return(SQR(u*GFACT*GFACT)*exp(-SQR(u*GFACT)/2));
+  if (ker==WPARM) return(0.0);
+  if (u>1) return(0.0);
+  switch(ker)
+  { case WBISQ: return(12*u*u);
+    case WTCUB: v = 1-u*u*u;
+                return(-9*u*v*v+54*u*u*u*u*v);
+    case WTRWT: return(24*u*u*(1-u*u));
+    default: ERROR(("Invalid kernel %d in Wdd",ker));
+  }
+  return(0.0);
+}
+
+/* int u1^j1..ud^jd W(u) du.
+   Used for local log-linear density estimation.
+   Assume all j_i are even.
+   Also in some bandwidth selection.
+*/
+double wint(d,j,nj,ker)
+INT d, *j, nj, ker;
+{ double I, z;
+  int k, dj;
+  dj = d;
+    I = 0.0;
+  for (k=0; k<nj; k++) dj += j[k];
+  switch(ker) /* int_0^1 u^(dj-1) W(u)du  */
+  { case WRECT: I = 1.0/dj; break;
+    case WEPAN: I = 2.0/(dj*(dj+2)); break;
+    case WBISQ: I = 8.0/(dj*(dj+2)*(dj+4)); break;
+    case WTCUB: I = 162.0/(dj*(dj+3)*(dj+6)*(dj+9)); break;
+    case WTRWT: I = 48.0/(dj*(dj+2)*(dj+4)*(dj+6)); break;
+    case WTRIA: I = 1.0/(dj*(dj+1)); break;
+    case WQUQU: I = 384.0/(dj*(dj+2)*(dj+4)*(dj+6)*(dj+8)); break;
+    case W6CUB: I = 524880.0/(dj*(dj+3)*(dj+6)*(dj+9)*(dj+12)*(dj+15)*(dj+18)); break;
+    case WGAUS: switch(d)
+                { case 1: I = S2PI/GFACT; break;
+                  case 2: I = 2*PI/(GFACT*GFACT); break;
+                  default: I = exp(d*log(S2PI/GFACT)); /* for nj=0 */
+                }
+                for (k=0; k<nj; k++) /* deliberate drop */
+                  switch(j[k])
+                  { case 4: I *= 3.0/(GFACT*GFACT);
+                    case 2: I /= GFACT*GFACT;
+                  }
+                return(I);
+    case WEXPL: I = factorial(dj-1)/ipower(EFACT,dj); break;
+    default: ERROR(("Unknown kernel %d in exacint",ker));
+  }
+  if ((d==1) && (nj==0)) return(2*I); /* common case quick */
+  z = (d-nj)*LOGPI/2-LGAMMA(dj/2.0);
+  for (k=0; k<nj; k++) z += LGAMMA((j[k]+1)/2.0);
+  return(2*I*exp(z));
+}
+
+/* taylor series expansion of weight function around x.
+   0 and 1 are common arguments, so are worth programming
+   as special cases.
+   Used in density estimation.
+*/
+INT wtaylor(f,x,ker)
+double *f, x;
+INT ker;
+{ double v;
+  switch(ker)
+  { case WRECT:
+      f[0] = 1.0;
+      return(1);
+    case WEPAN:
+      f[0] = 1-x*x; f[1] = -2*x; f[2] = -1;
+      return(3);
+    case WBISQ:
+      v = 1-x*x;
+      f[0] = v*v;   f[1] = -4*x*v; f[2] = 4-6*v;
+      f[3] = 4*x;   f[4] = 1;
+      return(5);
+    case WTCUB:
+      if (x==1.0)
+      { f[0] = f[1] = f[2] = 0; f[3] = -27; f[4] = -81; f[5] = -108;
+        f[6] = -81; f[7] = -36; f[8] = -9; f[9] = -1; return(10); }
+      if (x==0.0)
+      { f[1] = f[2] = f[4] = f[5] = f[7] = f[8] = 0;
+        f[0] = 1; f[3] = -3; f[6] = 3; f[9] = -1; return(10); }
+      v = 1-x*x*x;
+      f[0] = v*v*v; f[1] = -9*v*v*x*x; f[2] = x*v*(27-36*v);
+      f[3] = -27+v*(108-84*v);         f[4] = -3*x*x*(27-42*v);
+      f[5] = x*(-108+126*v);           f[6] = -81+84*v;
+      f[7] = -36*x*x; f[8] = -9*x;     f[9] = -1;
+      return(10);
+    case WTRWT:
+      v = 1-x*x;
+      f[0] = v*v*v; f[1] = -6*x*v*v; f[2] = v*(12-15*v);
+      f[3] = x*(20*v-8); f[4] = 15*v-12; f[5] = -6; f[6] = -1;
+      return(7);
+    case WTRIA:
+      f[0] = 1-x; f[1] = -1;
+      return(2);
+    case WQUQU:
+      v = 1-x*x;
+      f[0] = v*v*v*v; f[1] = -8*x*v*v*v; f[2] = v*v*(24-28*v);
+      f[3] = v*x*(56*v-32); f[4] = (70*v-80)*v+16; f[5] = x*(32-56*v);
+      f[6] = 24-28*v; f[7] = 8*x; f[8] = 1;
+      return(9);
+    case W6CUB:
+      v = 1-x*x*x;
+      f[0] = v*v*v*v*v*v;
+      f[1] = -18*x*x*v*v*v*v*v;
+      f[2] = x*v*v*v*v*(135-153*v);
+      f[3] = v*v*v*(-540+v*(1350-816*v));
+      f[4] = x*x*v*v*(1215-v*(4050-v*3060));
+      f[5] = x*v*(-1458+v*(9234+v*(-16254+v*8568)));
+      f[6] = 729-v*(10206-v*(35154-v*(44226-v*18564)));
+      f[7] = x*x*(4374-v*(30132-v*(56862-v*31824)));
+      f[8] = x*(12393-v*(61479-v*(92664-v*43758)));
+      f[9] = 21870-v*(89100-v*(115830-v*48620));
+      f[10]= x*x*(26730-v*(69498-v*43758));
+      f[11]= x*(23814-v*(55458-v*31824));
+      f[12]= 15849-v*(34398-v*18564);
+      f[13]= x*x*(7938-8568*v);
+      f[14]= x*(2970-3060*v);
+      f[15]= 810-816*v;
+      f[16]= 153*x*x;
+      f[17]= 18*x;
+      f[18]= 1;
+      return(19);
+  }
+  ERROR(("Invalid kernel %d in wtaylor",ker));
+  return(0);
+}
+
+/* convolution int W(x)W(x+v)dx.
+   used in kde bandwidth selection.
+*/
+double Wconv(v,ker)
+double v;
+INT ker;
+{ double v2;
+  switch(ker)
+  { case WGAUS: return(SQRPI/GFACT*exp(-SQR(GFACT*v)/4));
+    case WRECT:
+      v = fabs(v);
+      if (v>2) return(0.0);
+      return(2-v);
+    case WEPAN:
+      v = fabs(v);
+      if (v>2) return(0.0);
+      return((2-v)*(16+v*(8-v*(16-v*(2+v))))/30);
+    case WBISQ:
+      v = fabs(v);
+      if (v>2) return(0.0);
+      v2 = 2-v;
+      return(v2*v2*v2*v2*v2*(16+v*(40+v*(36+v*(10+v))))/630);
+  }
+  ERROR(("Wconv not implemented for kernel %d",ker));
+  return(0.0);
+}
+
+/* derivative of Wconv.
+   1/v d/dv int W(x)W(x+v)dx
+   used in kde bandwidth selection.
+*/
+double Wconv1(v,ker)
+double v;
+INT ker;
+{ double v2;
+  v = fabs(v);
+  switch(ker)
+  { case WGAUS: return(-0.5*SQRPI*GFACT*exp(-SQR(GFACT*v)/4));
+    case WRECT:
+      if (v>2) return(0.0);
+      return(1.0);
+    case WEPAN:
+      if (v>2) return(0.0);
+      return((-16+v*(12-v*v))/6);
+    case WBISQ:
+      if (v>2) return(0.0);
+      v2 = 2-v;
+      return(-v2*v2*v2*v2*(32+v*(64+v*(24+v*3)))/210);
+  }
+  ERROR(("Wconv1 not implemented for kernel %d",ker));
+  return(0.0);
+}
+
+/* 4th derivative of Wconv.
+   used in kde bandwidth selection (BCV, SJPI, GKK)
+*/
+double Wconv4(v,ker)
+double v;
+INT ker;
+{ double gv;
+  switch(ker)
+  { case WGAUS:
+      gv = GFACT*v;
+      return(exp(-SQR(gv)/4)*GFACT*GFACT*GFACT*(12-gv*gv*(12-gv*gv))*SQRPI/16);
+  }
+  ERROR(("Wconv4 not implemented for kernel %d",ker));
+  return(0.0);
+}
+
+/* 5th derivative of Wconv.
+   used in kde bandwidth selection (BCV method only)
+*/
+double Wconv5(v,ker) /* (d/dv)^5 int W(x)W(x+v)dx */
+double v;
+INT ker;
+{ double gv;
+  switch(ker)
+  { case WGAUS:
+      gv = GFACT*v;
+      return(-exp(-SQR(gv)/4)*GFACT*GFACT*GFACT*GFACT*gv*(60-gv*gv*(20-gv*gv))*SQRPI/32);
+  }
+  ERROR(("Wconv5 not implemented for kernel %d",ker));
+  return(0.0);
+}
+
+/* 6th derivative of Wconv.
+   used in kde bandwidth selection (SJPI)
+*/
+double Wconv6(v,ker)
+double v;
+INT ker;
+{ double gv, z;
+  switch(ker)
+  { case WGAUS:
+      gv = GFACT*v;
+      gv = gv*gv;
+      z = exp(-gv/4)*(-120+gv*(180-gv*(30-gv)))*0.02769459142;
+      gv = GFACT*GFACT;
+      return(z*gv*gv*GFACT);
+  }
+  ERROR(("Wconv6 not implemented for kernel %d",ker));
+  return(0.0);
+}
+
+/* int W(v)^2 dv / (int v^2 W(v) dv)^2
+   used in some bandwidth selectors
+*/
+double Wikk(ker,deg)
+INT ker, deg;
+{ switch(deg)
+  { case 0:
+    case 1: /* int W(v)^2 dv / (int v^2 W(v) dv)^2 */
+      switch(ker)
+      { case WRECT: return(4.5);
+        case WEPAN: return(15.0);
+        case WBISQ: return(35.0);
+        case WGAUS: return(0.2820947918*GFACT*GFACT*GFACT*GFACT*GFACT);
+        case WTCUB: return(34.15211105);
+        case WTRWT: return(66.08391608);
+      }
+    case 2:
+    case 3: /* 4!^2/8*int(W1^2)/int(v^4W1)^2
+               W1=W*(n4-v^2n2)/(n0n4-n2n2) */
+      switch(ker)
+      { case WRECT: return(11025.0);
+        case WEPAN: return(39690.0);
+        case WBISQ: return(110346.9231);
+        case WGAUS: return(14527.43412);
+        case WTCUB: return(126500.5904);
+        case WTRWT: return(254371.7647);
+      }
+  }
+  ERROR(("Wikk not implemented for kernel %d",ker));
+  return(0.0);
+}
diff --git a/src/multireads.cpp b/src/multireads.cpp
new file mode 100644
index 0000000..f23a0da
--- /dev/null
+++ b/src/multireads.cpp
@@ -0,0 +1,125 @@
+/*
+ *  multireads.cpp
+ *  cufflinks
+ *
+ *  Created by Adam Roberts on 3/6/11.
+ *  Copyright 2011 Adam Roberts. All rights reserved.
+ *
+ */
+
+
+#include "hits.h"
+#include "multireads.h"
+
+void MultiRead::add_hit(RefID r_id, int left, int right)
+{
+	_hits.push_back(MultiHit(r_id, left, right));
+}
+
+MultiHit* MultiRead::get_hit(RefID r_id, int left, int right)
+{
+    for (size_t i = 0; i < num_hits(); ++i)
+	{
+		MultiHit& hit = _hits[_curr_index];
+		if (hit.r_id == r_id && hit.left == left && hit.right == right)
+		{
+			return &hit;
+        }
+		_curr_index = (_curr_index + 1) % num_hits();
+	}  
+    fprintf(stderr, "\nERROR: Multi-Hit not found (%d,%d).\n", left, right); 
+    exit(1);
+}
+
+void MultiRead::add_expr(RefID r_id, int left, int right, double expr)
+{
+	MultiHit* hit = get_hit(r_id, left, right);
+    hit->expr += expr;
+	_tot_expr += expr;
+}
+
+double MultiRead::get_mass(RefID r_id, int left, int right, bool valid_mass)
+{
+	if (!valid_mass)
+	{
+		return 1.0/num_hits();
+	}
+	
+	if (_tot_expr == 0.0)
+		return 0.0;
+	
+	MultiHit* hit = get_hit(r_id, left, right);
+	return hit->expr/_tot_expr;
+}
+
+MultiRead* MultiReadTable::get_read(InsertID mr_id)
+{
+	MultiReadMap::iterator it;
+	it = _read_map.find(mr_id);
+	if (it == _read_map.end())
+	{
+		return NULL;
+	}
+	else 
+	{
+		return &(it->second);
+	}
+}
+
+void MultiReadTable::add_hit(const MateHit& hit)
+{
+	add_hit(hit.ref_id(), hit.left(), hit.right(), hit.insert_id(), hit.num_hits());
+}
+
+void MultiReadTable::add_hit(RefID r_id, int left, int right, InsertID mr_id, int exp_num_hits)
+{
+#if ENABLE_THREADS
+	boost::mutex::scoped_lock lock(_lock);
+#endif
+	MultiRead* mr = get_read(mr_id);
+	if (!mr)
+	{
+		mr = &((_read_map.insert(std::make_pair(mr_id, MultiRead(mr_id, exp_num_hits)))).first->second); 
+	}
+	mr->add_hit(r_id, left, right);
+}
+
+void MultiReadTable::add_expr(const MateHit& hit, double expr)
+{
+	add_expr(hit.ref_id(), hit.left(), hit.right(), hit.insert_id(), expr);
+}
+
+void MultiReadTable::add_expr(RefID r_id, int left, int right, InsertID mr_id, double expr)
+{
+#if ENABLE_THREADS
+	boost::mutex::scoped_lock lock(_lock);
+#endif
+	MultiRead* mr = get_read(mr_id);
+	mr->add_expr(r_id, left, right, expr);
+}
+
+double MultiReadTable::get_mass(const MateHit& hit)
+{
+#if ENABLE_THREADS
+	boost::mutex::scoped_lock lock(_lock);
+#endif	
+	MultiRead* mr = get_read(hit.insert_id());
+	if(!mr)
+		return 1.0;
+	return mr->get_mass(hit.ref_id(), hit.left(), hit.right(), _valid_mass);
+}
+
+size_t MultiReadTable::num_multireads()
+{
+	return (int)_read_map.size();
+}
+
+size_t MultiReadTable::num_multihits()
+{
+	size_t count = 0;
+	for (MultiReadMap::iterator it=_read_map.begin() ; it != _read_map.end(); it++ )
+	{
+		count += it->second.num_hits();
+	}
+	return count;
+}
diff --git a/src/multireads.h b/src/multireads.h
new file mode 100644
index 0000000..da017ad
--- /dev/null
+++ b/src/multireads.h
@@ -0,0 +1,72 @@
+#ifndef MULTIREADS_H
+#define MULTIREADS_H
+
+#include <boost/thread.hpp>
+
+typedef uint64_t RefID;
+typedef uint64_t InsertID;
+
+struct MultiHit
+{
+    MultiHit(RefID id, int l, int r)
+    :   r_id(id),
+        left(l),
+        right(r),
+        expr(0) {}
+    RefID r_id;
+    int left;
+    int right;
+    double expr;
+};
+
+class MultiRead
+{
+	size_t _curr_index;
+	std::vector<MultiHit> _hits;
+	double _tot_expr;
+	InsertID _id;
+    
+    MultiHit* get_hit(RefID r_id, int left, int right);
+	
+public:
+	
+	MultiRead(InsertID id, int exp_num_hits)
+	:	_curr_index(0),
+		_tot_expr(0.0),
+		_id(id)
+	{
+		_hits.reserve(exp_num_hits);
+	}
+	
+	size_t num_hits() { return (int)_hits.size(); }
+	void add_hit(RefID r_id, int left, int right);
+	void add_expr(RefID r_id, int left, int right, double expr);
+	double get_mass(RefID r_id, int left, int right, bool valid_mass);
+};
+
+class MateHit;
+
+class MultiReadTable
+{
+	typedef std::map<InsertID, MultiRead> MultiReadMap;
+	MultiReadMap _read_map;
+	bool _valid_mass;
+	MultiRead* get_read(InsertID mr_id);
+#if ENABLE_THREADS
+	boost::mutex _lock;
+#endif
+public:
+	MultiReadTable(): _valid_mass(false) {}
+	
+	void valid_mass(bool vm) { _valid_mass = vm; }
+	void add_hit(const MateHit& hit);
+	void add_hit(RefID r_id, int left, int right, InsertID mr_id, int exp_num_hits);
+	void add_expr(const MateHit& hit, double expr);
+	void add_expr(RefID r_id, int left, int right, InsertID mr_id, double expr);
+	double get_mass(const MateHit& hit);
+	size_t num_multireads();
+	size_t num_multihits();
+	
+};
+
+#endif
diff --git a/src/progressbar.h b/src/progressbar.h
index 2df226b..d5b953d 100644
--- a/src/progressbar.h
+++ b/src/progressbar.h
@@ -1,8 +1,8 @@
 #ifndef PROGRESS_H
 #define PROGRESS_H
 
-#include "common.h"
 #include "time.h"
+
 using namespace std;
 
 const int BAR_BUF_SIZE = 28;
@@ -12,17 +12,20 @@ class ProgressBar
 {
 	char _bar_buf[BAR_BUF_SIZE];
 	string _process;
-	int _num_complete;
-	int _tot_num;
+	long double _num_complete;
+	long double _tot_num;
 	int _num_updates;
+	int _num_remaining;
+	
 public:
 	ProgressBar() {}
 	
-	ProgressBar(string process, int tot_num) 
+	ProgressBar(string process, double tot_num) 
 	{ 
 		_tot_num = tot_num;
 		_process = process;
-		_num_complete = -1;
+		_num_complete = -1.0;
+		_num_remaining = -1.0;
 		_num_updates = 0;
 		
 		for(int i=0; i < BAR_BUF_SIZE; ++i) _bar_buf[i] = ' ';
@@ -43,26 +46,51 @@ public:
 		fprintf(stderr, "[%s] %s\n", time_buf, _process.c_str());
 	}
 	
-	void update(const char* bundle_label_buf, int inc_amt)
+	void update(const char* bundle_label_buf, double inc_amt)
 	{
+		
 		_num_complete += inc_amt;
 		_num_updates ++;
 		
 		if (cuff_verbose||cuff_quiet||_tot_num==0) return;
 
 		char bundle_buf[28];
+		bundle_buf[27] = '\0';
 		strncpy(bundle_buf, bundle_label_buf, 27);
 		
 		int percent = (_num_complete * 100)/_tot_num;
-		percent = min(percent, 100);
+
+		percent = min(percent, 99);
 		
 		int last_bar = percent/(100/(BAR_BUF_SIZE-3));
 		for (int i=1; i <= last_bar; ++i)
 			_bar_buf[i] = SYMBOL;
 		
 		char line_buf[82];
-		snprintf(line_buf, 81, "\r> Processing Locus %-27s %s %3d%%", bundle_buf, _bar_buf, percent);
-
+		snprintf(line_buf, 82, "\r> Processing Locus %-27s %s %3d%%", bundle_buf, _bar_buf, percent);
+		
+		fprintf(stderr,"%s",line_buf);
+	}
+	
+	void remaining(int num_remaining)
+	{
+		if (cuff_verbose||cuff_quiet||_tot_num==0||num_remaining==_num_remaining) return;
+		
+		_num_remaining = num_remaining;
+		
+		int percent = (_num_complete * 100)/_tot_num;
+		percent = min(percent, 99);
+		
+		char msg_buff[45];
+		sprintf(msg_buff, "Waiting for %d threads to complete.", num_remaining);
+		
+		int last_bar = percent/(100/(BAR_BUF_SIZE-3));
+		for (int i=1; i <= last_bar; ++i)
+			_bar_buf[i] = SYMBOL;
+		
+		char line_buf[82];
+		snprintf(line_buf, 81, "\r> %-44s %s %3d%%", msg_buff, _bar_buf, percent);
+		
 		fprintf(stderr,"%s",line_buf);
 	}
 	
@@ -73,7 +101,7 @@ public:
 		char complete_buf[45];
 		snprintf(complete_buf, 44, "Processed %d loci.", _num_updates); 
 		if (cuff_verbose||cuff_quiet)
-			fprintf(stderr, "%-44s", complete_buf);
+			fprintf(stderr, "%-44s\n", complete_buf);
 		else
 			fprintf(stderr, "\r> %-44s %s %3d%%\n", complete_buf, _bar_buf, 100);
 	}
diff --git a/src/replicates.cpp b/src/replicates.cpp
new file mode 100644
index 0000000..852f6ac
--- /dev/null
+++ b/src/replicates.cpp
@@ -0,0 +1,306 @@
+//
+//  replicates.cpp
+//  cufflinks
+//
+//  Created by Cole Trapnell on 3/11/11.
+//  Copyright 2011 Cole Trapnell. All rights reserved.
+//
+
+#include <boost/thread.hpp>
+
+extern "C" {
+#include "locfit/local.h"
+}
+
+#include "replicates.h"
+
+#if ENABLE_THREADS	
+boost::mutex _locfit_lock;
+#endif
+
+double MassDispersionModel::scale_mass_variance(double scaled_mass) const 
+{
+    if (scaled_mass <= 0)
+        return 0.0;
+    
+    if (_scaled_mass_means.size() < 2 || _scaled_mass_variances.size() < 2)
+    {
+        return scaled_mass; // revert to poisson.
+    }
+    if (scaled_mass > _scaled_mass_means.back())
+    {
+        // extrapolate to the right
+        // off the right end
+        double x1_mean = _scaled_mass_means[_scaled_mass_means.size()-2];
+        double x2_mean = _scaled_mass_means[_scaled_mass_means.size()-1];
+        
+        double y1_var = _scaled_mass_variances[_scaled_mass_means.size()-2];
+        double y2_var = _scaled_mass_variances[_scaled_mass_means.size()-1];
+        double slope = 0.0;                
+        if (x2_mean != x1_mean)
+        {
+            slope = (y2_var - y1_var) / (x2_mean-x1_mean);
+        }
+        else if (y1_var == y2_var)
+        {
+            assert (false); // should have a unique'd table
+        }
+        double mean_interp = _scaled_mass_variances[_scaled_mass_means.size()-1] -
+        slope*(scaled_mass - _scaled_mass_means.size()-1);
+        if (mean_interp < scaled_mass)
+            mean_interp = scaled_mass;
+        assert (!isnan(mean_interp) && !isinf(mean_interp));
+        return mean_interp;
+    }
+    else if (scaled_mass < _scaled_mass_means.front())
+    {
+        // extrapolate to the left
+        // off the left end?
+        double x1_mean = _scaled_mass_means[0];
+        double x2_mean = _scaled_mass_means[1];
+        
+        double y1_var = _scaled_mass_variances[0];
+        double y2_var = _scaled_mass_variances[1];
+        double slope = 0.0;                
+        if (x2_mean != x1_mean)
+        {
+            slope = (y2_var - y1_var) / (x2_mean-x1_mean);
+        }
+        else if (y1_var == y2_var)
+        {
+            assert (false); // should have a unique'd table
+        }
+        double mean_interp = _scaled_mass_variances[0] - slope*(_scaled_mass_means[0] - scaled_mass);
+        if (mean_interp < scaled_mass)
+            mean_interp = scaled_mass;
+
+        assert (!isnan(mean_interp) && !isinf(mean_interp));
+        return mean_interp;
+    }
+    
+    vector<double>::const_iterator lb;
+    lb = lower_bound(_scaled_mass_means.begin(), 
+                     _scaled_mass_means.end(), 
+                     scaled_mass);
+    if (lb != _scaled_mass_means.end())
+    {
+        int d = lb - _scaled_mass_means.begin();
+        if (*lb == scaled_mass)
+        {
+            double var = _scaled_mass_variances[d];
+            if (var < scaled_mass) // revert to poisson if underdispersed
+                var = scaled_mass;
+            assert (!isnan(var) && !isinf(var));
+            return var;
+        }
+        
+        //in between two points on the scale.
+        d--;
+        double x1_mean = _scaled_mass_means[d];
+        double x2_mean = _scaled_mass_means[d + 1];
+        
+        double y1_var = _scaled_mass_variances[d];
+        double y2_var = _scaled_mass_variances[d + 1];
+        double slope = 0.0;                
+        if (x2_mean != x1_mean)
+        {
+            slope = (y2_var - y1_var) / (x2_mean-x1_mean);
+        }
+        else if (y1_var == y2_var)
+        {
+            assert (false); // should have a unique'd table
+        }
+        double mean_interp = _scaled_mass_variances[d] + slope*(scaled_mass - _scaled_mass_means[d]);
+        if (mean_interp < scaled_mass) // revert to poisson if underdispersed
+            mean_interp = scaled_mass;
+ 
+        assert (!isnan(mean_interp) && !isinf(mean_interp));
+        return mean_interp;
+    }
+    else
+    {
+        assert (!isnan(scaled_mass) && !isinf(scaled_mass));
+        return scaled_mass; // revert to poisson assumption
+    }
+}
+
+void calc_scaling_factors(const vector<pair<string, vector<double> > >& sample_count_table,
+                          vector<double>& scale_factors)
+{
+    vector<double> geom_means(sample_count_table.size(), 0.0);
+    
+    for (size_t i = 0; i < sample_count_table.size(); ++i)
+    {
+        const pair<string, vector<double> >& p = sample_count_table[i];
+        
+        for (size_t j = 0; j < p.second.size(); ++j)
+        {
+            //assert (geom_means.size() > j);
+            if (geom_means[i] > 0  && p.second[j] > 0)
+            {
+                geom_means[i] *= p.second[j];
+            }
+            else if (p.second[j] > 0)
+            {
+                geom_means[i] = p.second[j];
+            }
+            
+        }
+        geom_means[i] = pow(geom_means[i], 1.0/(double)p.second.size());
+    }
+    
+    for (size_t j = 0; j < scale_factors.size(); ++j)
+    {
+        vector<double> tmp_counts;
+        for (size_t i = 0; i < sample_count_table.size(); ++i)
+        {
+            if (geom_means[i] && !isinf(geom_means[i]) && !isnan(geom_means[i]) && sample_count_table[i].second[j])
+            {
+                double gm = (double)sample_count_table[i].second[j] / geom_means[i];
+                assert (!isinf(gm));
+                tmp_counts.push_back(gm);
+            }
+        }
+        sort(tmp_counts.begin(), tmp_counts.end());
+        if (!tmp_counts.empty())
+            scale_factors[j] = tmp_counts[tmp_counts.size()/2];
+        else
+            scale_factors[j] = 1.0;
+    }
+}
+
+boost::shared_ptr<MassDispersionModel const> 
+fit_dispersion_model(const string& condition_name,
+                     const vector<double>& scale_factors,
+                     const vector<pair<string, vector<double> > >& sample_count_table)
+{
+//    
+//#if ENABLE_THREADS
+//	boost::mutex::scoped_lock lock(_locfit_lock);
+//#endif
+    
+    vector<pair<double, double> > raw_means_and_vars;
+    
+    for (size_t i = 0; i < sample_count_table.size(); ++i)
+    {
+        if (sample_count_table[i].second.size() <= 1)
+        {
+            // only one replicate - no point in fitting variance
+            return shared_ptr<MassDispersionModel const>(new PoissonDispersionModel);
+        }
+    }
+#if ENABLE_THREADS
+    _locfit_lock.lock();
+#endif
+    
+    ProgressBar p_bar("Modeling fragment count overdispersion.",0);
+    
+    for (size_t i = 0; i < sample_count_table.size(); ++i)
+    {
+        const pair<string, vector<double> >& p = sample_count_table[i];
+        double mean = accumulate(p.second.begin(), p.second.end(), 0.0);
+        if (mean > 0.0 && p.second.size() > 0)
+            mean /= p.second.size();
+        
+        double var = 0.0;
+        foreach (double d, p.second)
+        {
+            var += (d - mean) * (d - mean);
+        }
+        if (var > 0.0 && p.second.size())
+            var /= p.second.size();
+        if (mean > 0 && var > 0.0)
+            raw_means_and_vars.push_back(make_pair(mean, var));
+    }
+    
+    if (raw_means_and_vars.empty())
+    {
+        fprintf(stderr, "Warning: fragment count variances between replicates are all zero, reverting to Poisson model\n");
+#if ENABLE_THREADS
+        _locfit_lock.unlock();
+#endif
+        return shared_ptr<MassDispersionModel const>(new PoissonDispersionModel);
+    }
+    
+    sort(raw_means_and_vars.begin(), raw_means_and_vars.end());
+    
+    vector<double> raw_means(raw_means_and_vars.size(), 0.0);
+    vector<double> raw_variances(raw_means_and_vars.size(), 0.0);
+    
+    for(size_t i = 0; i < raw_means_and_vars.size(); ++i)
+    {
+        raw_means[i] = raw_means_and_vars[i].first;
+        raw_variances[i] = raw_means_and_vars[i].second;
+    }
+    
+    vector<double> fitted_values(raw_means_and_vars.size(), 0.0);
+    
+    setuplf();  
+    
+    // WARNING: locfit doesn't like undescores - need camel case for 
+    // variable names
+    
+    char namebuf[256];
+    sprintf(namebuf, "countMeans");
+    vari* cm = createvar(namebuf,STREGULAR,raw_means.size(),VDOUBLE);
+    for (size_t i = 0; i < raw_means.size(); ++i)
+    {
+        cm->dpr[i] = log(raw_means[i]);
+    }
+    
+    sprintf(namebuf, "countVariances");
+    vari* cv = createvar(namebuf,STREGULAR,raw_variances.size(),VDOUBLE);
+    for (size_t i = 0; i < raw_variances.size(); ++i)
+    {
+        cv->dpr[i] = raw_variances[i]; 
+    }
+    
+    char locfit_cmd[2048];
+    sprintf(locfit_cmd, "locfit countVariances~countMeans family=gamma");
+    
+    locfit_dispatch(locfit_cmd);
+    
+    sprintf(locfit_cmd, "fittedVars=predict countMeans");
+    locfit_dispatch(locfit_cmd);
+    
+    //sprintf(locfit_cmd, "prfit x fhat h nlx");
+    //locfit_dispatch(locfit_cmd);
+    
+    int n = 0;
+    sprintf(namebuf, "fittedVars");
+    vari* cp = findvar(namebuf, 1, &n);
+    assert(cp != NULL);
+    for (size_t i = 0; i < cp->n; ++i)
+    {
+        fitted_values[i] = cp->dpr[i];
+    }
+    
+    shared_ptr<MassDispersionModel> disperser;
+    disperser = shared_ptr<MassDispersionModel>(new MassDispersionModel(raw_means, fitted_values));
+    if (poisson_dispersion)
+        disperser = shared_ptr<MassDispersionModel>(new PoissonDispersionModel);
+
+    if (emit_count_tables)
+    {
+        string cond_count_filename = output_dir + "/" + condition_name + "_counts.txt";
+        
+        FILE* sample_count_file = fopen(cond_count_filename.c_str(), "w");
+        
+        if (sample_count_file)
+        {
+            fprintf(sample_count_file, "count_mean\tcount_var\tfitted_var\n");
+            for (size_t i = 0; i < raw_means_and_vars.size(); ++i)
+            {
+                fprintf(sample_count_file, "%lg\t%lg\t%lg\n", 
+                        raw_means_and_vars[i].first, 
+                        raw_means_and_vars[i].second,
+                        fitted_values[i]);
+            }
+            fclose(sample_count_file);
+        } 
+    }
+#if ENABLE_THREADS
+    _locfit_lock.unlock();
+#endif
+    return disperser;
+}
diff --git a/src/replicates.h b/src/replicates.h
new file mode 100644
index 0000000..00db40d
--- /dev/null
+++ b/src/replicates.h
@@ -0,0 +1,274 @@
+//
+//  replicates.h
+//  cufflinks
+//
+//  Created by Cole Trapnell on 3/11/11.
+//  Copyright 2011 Cole Trapnell. All rights reserved.
+//
+
+#include "common.h"
+#include "bundles.h"
+#include <vector>
+#include <algorithm>
+
+class MassDispersionModel
+{
+public:
+    MassDispersionModel() {}
+    MassDispersionModel(const std::vector<double>& scaled_mass_means, 
+                        const std::vector<double>& scaled_mass_variances) :
+    _scaled_mass_means(scaled_mass_means),
+    _scaled_mass_variances(scaled_mass_variances) 
+    {
+        std::vector<double>::iterator new_end = unique(_scaled_mass_means.begin(), _scaled_mass_means.end());
+        _scaled_mass_means.erase(new_end, _scaled_mass_means.end());
+        
+        new_end = unique(_scaled_mass_variances.begin(), _scaled_mass_variances.end());
+        _scaled_mass_variances.erase(new_end, _scaled_mass_variances.end());
+    }
+    
+    virtual double scale_mass_variance(double scaled_mass) const;
+    
+private:
+    std::vector<double> _scaled_mass_means;
+    std::vector<double> _scaled_mass_variances;
+};
+
+class PoissonDispersionModel : public MassDispersionModel
+{
+public:
+    
+    virtual double scale_mass_variance(double scaled_mass) const 
+    { 
+        return scaled_mass; 
+    }
+};
+
+void calc_scaling_factors(const std::vector<pair<std::string, std::vector<double> > >& sample_count_table,
+                          std::vector<double>& scale_factors);
+
+boost::shared_ptr<MassDispersionModel const> 
+fit_dispersion_model(const string& condition_name,
+                     const std::vector<double>& scale_factors,
+                     const std::vector<std::pair<std::string, std::vector<double> > >& sample_count_table);
+
+// This factory merges bundles in a requested locus from several replicates
+class ReplicatedBundleFactory
+{
+public:
+	ReplicatedBundleFactory(const std::vector<shared_ptr<BundleFactory> >& factories, 
+                            const string& condition_name)
+    : _factories(factories), _condition_name(condition_name) {}
+	
+	int num_bundles() { return _factories[0]->num_bundles(); }
+    std::vector<boost::shared_ptr<BundleFactory> > factories() { return _factories; }
+	
+    const string& condition_name() const { return _condition_name; }
+    void condition_name(const string& cn) { _condition_name = cn; }
+    
+    bool bundles_remain() 
+    {
+#if ENABLE_THREADS
+        boost::mutex::scoped_lock lock(_rep_factory_lock);
+#endif
+        foreach (boost::shared_ptr<BundleFactory> fac, _factories)
+        {
+            if (fac->bundles_remain())
+                return true;
+        }
+        return false;
+    }
+    
+	bool next_bundle(HitBundle& bundle_out)
+    {
+#if ENABLE_THREADS
+        boost::mutex::scoped_lock lock(_rep_factory_lock);
+#endif
+        std::vector<HitBundle*> bundles;
+        
+        bool non_empty_bundle = false;
+        foreach (boost::shared_ptr<BundleFactory> fac, _factories)
+        {
+            bundles.push_back(new HitBundle());
+            if (fac->next_bundle(*(bundles.back())))
+            {
+                non_empty_bundle = true;
+            }
+        }
+        
+        if (non_empty_bundle == false)
+        {
+            foreach (HitBundle* in_bundle, bundles)
+            {
+                in_bundle->ref_scaffolds().clear();
+                in_bundle->clear_hits();
+                delete in_bundle;
+            }
+            return false;
+        }
+        
+        for (size_t i = 1; i < bundles.size(); ++i)
+        {
+            const vector<shared_ptr<Scaffold> >& s1 = bundles[i]->ref_scaffolds();
+            const vector<shared_ptr<Scaffold> >& s2 =  bundles[i-1]->ref_scaffolds();
+            assert (s1.size() == s2.size());
+            for (size_t j = 0; j < s1.size(); ++j)
+            {
+                assert (s1[j]->annotated_trans_id() == s2[j]->annotated_trans_id());
+            }
+        }
+        
+        // Merge the replicates into a combined bundle of hits.
+        HitBundle::combine(bundles, bundle_out);
+        
+        foreach (HitBundle* in_bundle, bundles)
+        {
+            in_bundle->ref_scaffolds().clear();
+            in_bundle->clear_hits();
+            delete in_bundle;
+        }
+        return true;
+    }
+	
+	void reset() 
+    {
+#if ENABLE_THREADS
+        boost::mutex::scoped_lock lock(_rep_factory_lock);
+#endif
+        foreach (shared_ptr<BundleFactory> fac, _factories)
+        {
+            fac->reset();
+        }
+    }
+    
+    void inspect_replicate_maps(int& min_len, int& max_len)
+    {
+        vector<pair<string, vector<double> > > sample_count_table;
+        vector<double> sample_masses;
+        
+        foreach (shared_ptr<BundleFactory> fac, _factories)
+        {
+            BadIntronTable bad_introns;
+            
+            vector<pair<string, double> > count_table;
+            inspect_map(*fac, NULL, count_table, false);
+            
+            shared_ptr<ReadGroupProperties> rg_props = fac->read_group_properties();
+            
+            for (size_t i = 0; i < count_table.size(); ++i)
+            {
+                pair<string, double>& c = count_table[i];
+                double raw_count = c.second;
+                
+                if (i >= sample_count_table.size())
+                {
+                    sample_count_table.push_back(make_pair(c.first, vector<double>()));
+                    sample_count_table.back().second.push_back(raw_count);
+                }
+                else
+                {
+                    if (sample_count_table[i].first != c.first)
+                    {
+                        fprintf (stderr, "Error: bundle boundaries don't match across replicates!\n");
+                        exit(1);
+                    }
+                    sample_count_table[i].second.push_back(raw_count);
+                }
+            }
+            sample_masses.push_back(rg_props->total_map_mass());
+			min_len = min(min_len, rg_props->frag_len_dist()->min());
+			max_len = max(max_len, rg_props->frag_len_dist()->max());
+        }
+        
+        vector<double> scale_factors(_factories.size(), 0.0);
+        
+        calc_scaling_factors(sample_count_table, scale_factors);
+        
+        for (size_t i = 0; i < scale_factors.size(); ++i)
+        {
+            shared_ptr<ReadGroupProperties> rg_props = _factories[i]->read_group_properties();
+            assert (scale_factors[i] != 0);
+            rg_props->mass_scale_factor(scale_factors[i]);
+        }
+        
+        // Transform raw counts to the common scale
+        for (size_t i = 0; i < sample_count_table.size(); ++i)
+        {
+            pair<string, vector<double> >& p = sample_count_table[i];
+            for (size_t j = 0; j < p.second.size(); ++j)
+            {
+                assert (scale_factors.size() > j);
+                p.second[j] *= (1.0 / scale_factors[j]);
+            }
+        }
+        
+        for (size_t i = 0; i < _factories.size(); ++i)
+        {
+            shared_ptr<ReadGroupProperties> rg_props = _factories[i]->read_group_properties();
+            vector<pair<string, double> > scaled_counts;
+            for (size_t j = 0; j < sample_count_table.size(); ++j)
+            {
+                scaled_counts.push_back(make_pair(sample_count_table[j].first, sample_count_table[j].second[i]));
+            }
+            rg_props->common_scale_counts(scaled_counts);
+        }
+        
+        shared_ptr<MassDispersionModel const> disperser;
+        disperser = fit_dispersion_model(_condition_name,scale_factors, sample_count_table);
+        
+        foreach (shared_ptr<BundleFactory> fac, _factories)
+        {
+            shared_ptr<ReadGroupProperties> rg_props = fac->read_group_properties();
+            rg_props->mass_dispersion_model(disperser);
+        }
+    }
+    
+    // This function NEEDS to deep copy the ref_mRNAs, otherwise cuffdiff'd
+    // samples will clobber each other
+    void set_ref_rnas(const vector<shared_ptr<Scaffold> >& mRNAs)
+    {
+#if ENABLE_THREADS
+        boost::mutex::scoped_lock lock(_rep_factory_lock);
+#endif
+        foreach(shared_ptr<BundleFactory> fac, _factories)
+        {
+            fac->set_ref_rnas(mRNAs);
+        }
+    }
+    
+    void set_mask_rnas(const vector<shared_ptr<Scaffold> >& mRNAs)
+    {
+#if ENABLE_THREADS
+        boost::mutex::scoped_lock lock(_rep_factory_lock);
+#endif
+        foreach(shared_ptr<BundleFactory> fac, _factories)
+        {
+            fac->set_mask_rnas(mRNAs);
+        }
+    }
+    
+    int num_replicates() const { return _factories.size(); }
+    
+    void mass_dispersion_model(shared_ptr<MassDispersionModel const> disperser)
+    {
+#if ENABLE_THREADS
+        boost::mutex::scoped_lock lock(_rep_factory_lock);
+#endif
+        foreach(shared_ptr<BundleFactory>& fac, _factories)
+        {
+            fac->read_group_properties()->mass_dispersion_model(disperser);
+        }
+    }
+    
+    shared_ptr<MassDispersionModel const> mass_dispersion_model() const
+    {
+        return _factories.front()->read_group_properties()->mass_dispersion_model();
+    }
+    
+private:
+	vector<shared_ptr<BundleFactory> > _factories;
+#if ENABLE_THREADS
+    boost::mutex _rep_factory_lock;
+#endif
+    string _condition_name; 
+};
diff --git a/src/scaffold_graph.cpp b/src/scaffold_graph.cpp
index c777802..4cb917f 100644
--- a/src/scaffold_graph.cpp
+++ b/src/scaffold_graph.cpp
@@ -235,7 +235,7 @@ bool create_overlap_dag(vector<Scaffold>& hits,
                          tr, 
                          G_to_TR,
                          w);
-    asm_verbose("dag has %lu edges, tr has %lu edges\n", num_edges(bundle_dag), num_edges(tr));
+    verbose_msg("dag has %lu edges, tr has %lu edges\n", num_edges(bundle_dag), num_edges(tr));
     
 	//assert (num_edges(bundle_dag) == num_edges(tr));
 #endif
@@ -281,7 +281,7 @@ pair<DAGNode, DAGNode> add_terminal_nodes(DAG& bundle_dag)
         }
     }
     
-#if ASM_VERBOSE
+#if verbose_msg
     HitsForNodeMap hits_for_node = get(vertex_name, bundle_dag);
     DAG::vertex_iterator ki, ke;
 	for (tie(ki, ke) = vertices(bundle_dag); ki != ke; ++ki)
@@ -298,7 +298,7 @@ pair<DAGNode, DAGNode> add_terminal_nodes(DAG& bundle_dag)
             fprintf(stderr, "%d-%d has edge to sink\n", pS->left(), pS->right());
         }
     }
-    asm_verbose("%d source nodes, %d sink nodes\n", num_attached_to_source, num_attached_to_sink);
+    verbose_msg("%d source nodes, %d sink nodes\n", num_attached_to_source, num_attached_to_sink);
 #endif
     return make_pair(source, sink);
 }
diff --git a/src/scaffolds.cpp b/src/scaffolds.cpp
index a6d6406..096f58a 100644
--- a/src/scaffolds.cpp
+++ b/src/scaffolds.cpp
@@ -9,15 +9,16 @@
 
 #include <list>
 #include <algorithm>
+#include <numeric>
 #include "common.h"
 #include "scaffolds.h"
 
 using namespace std;
 
 bool AugmentedCuffOp::compatible(const AugmentedCuffOp& lhs,
-								 const AugmentedCuffOp& rhs)
+								 const AugmentedCuffOp& rhs,
+								 int overhang_tolerance)
 {
-	int l_match = match_length(lhs, rhs.g_left(), rhs.g_right());
 	if (rhs.opcode == CUFF_INTRON)
 	{
 		if (lhs.opcode == CUFF_INTRON)
@@ -28,23 +29,27 @@ bool AugmentedCuffOp::compatible(const AugmentedCuffOp& lhs,
 		}
 		else if (lhs.opcode == CUFF_UNKNOWN)
 		{
-            int left_diff = abs(lhs.g_left() - rhs.g_left());
-            int right_diff = abs(lhs.g_right() - rhs.g_right());
-				if (left_diff + right_diff > max_frag_len)
-					return false;
-			}
-		else if (l_match > bowtie_overhang_tolerance)
-		{
-			return false;
-		}
+            //int left_diff = abs(lhs.g_left() - rhs.g_left());
+            //int right_diff = abs(lhs.g_right() - rhs.g_right());
+			//if (left_diff + right_diff > max_frag_len)
+            //    return false;
+        }
+        else
+        {
+            int l_match = match_length(lhs, rhs.g_left(), rhs.g_right());
+            if (l_match > overhang_tolerance)
+            {
+                return false;
+            }
+        }
 	}
 	else if (rhs.opcode == CUFF_UNKNOWN)
 	{
+        int l_match = match_length(lhs, rhs.g_left(), rhs.g_right());
 		if (l_match > max_frag_len)
 			return false;
 	}
-	
-	int r_match = match_length(rhs, lhs.g_left(), lhs.g_right());
+
 	if (lhs.opcode == CUFF_INTRON)
 	{
 		if (rhs.opcode == CUFF_INTRON)
@@ -55,18 +60,23 @@ bool AugmentedCuffOp::compatible(const AugmentedCuffOp& lhs,
 		}
 		else if (lhs.opcode == CUFF_UNKNOWN)
 		{
-            int left_diff = abs(lhs.g_left() - rhs.g_left());
-            int right_diff = abs(lhs.g_right() - rhs.g_right());
-				if (left_diff + right_diff > max_frag_len)
-                return false;
-		}
-		else if (r_match > bowtie_overhang_tolerance)
-		{
-			return false;
+            //int left_diff = abs(lhs.g_left() - rhs.g_left());
+            //int right_diff = abs(lhs.g_right() - rhs.g_right());
+            //if (left_diff + right_diff > max_frag_len)
+            //    return false;
 		}
+		else 
+        {
+            int r_match = match_length(rhs, lhs.g_left(), lhs.g_right());
+            if (r_match > overhang_tolerance)
+            {
+                return false;
+            }
+        }
 	}	
 	else if (lhs.opcode == CUFF_UNKNOWN)
 	{
+        int r_match = match_length(rhs, lhs.g_left(), lhs.g_right());
 		if (r_match > max_frag_len)
 			return false;
 	}
@@ -127,7 +137,8 @@ void record_gaps(const vector<AugmentedCuffOp>& to_fill,
 // this function
 void AugmentedCuffOp::fill_interstices(vector<AugmentedCuffOp>& to_fill,
                                        const vector<AugmentedCuffOp>& filler,
-                                       bool allow_flank_fill)
+                                       bool allow_flank_fill,
+                                       bool allow_flanking_introns)
 {
 	vector<AugmentedCuffOp> filled = to_fill;
 	vector<pair<int, int> > gaps;
@@ -271,20 +282,21 @@ void AugmentedCuffOp::fill_interstices(vector<AugmentedCuffOp>& to_fill,
 	}
     
 	sort(filled.begin(), filled.end(), g_left_lt);
-    
-//    for (size_t i = 0; i < filled.size(); ++i)
-//    {
-//        if (filled[i].opcode == CUFF_INTRON)
-//        {
-//            assert (i > 0);
-//            assert (i < filled.size() -1);
-//            assert (filled[i-1].opcode == CUFF_MATCH);
-//            assert (filled[i+1].opcode == CUFF_MATCH);
-//            assert (filled[i-1].g_right() == filled[i].g_left());
-//            assert (filled[i+1].g_left() == filled[i].g_right());
-//        }
-//    }
-    
+    if (!allow_flanking_introns)
+    {
+        for (size_t i = 0; i < filled.size(); ++i)
+        {
+            if (filled[i].opcode == CUFF_INTRON)
+            {
+                assert (i > 0);
+                assert (i < filled.size() -1);
+                assert (filled[i-1].opcode == CUFF_MATCH);
+                assert (filled[i+1].opcode == CUFF_MATCH);
+                assert (filled[i-1].g_right() == filled[i].g_left());
+                assert (filled[i+1].g_left() == filled[i].g_right());
+            }
+        }
+    }
 	to_fill = filled;
 }
 
@@ -293,7 +305,8 @@ void AugmentedCuffOp::fill_interstices(vector<AugmentedCuffOp>& to_fill,
 // ops is assumed to be sorted
 void AugmentedCuffOp::merge_ops(const vector<AugmentedCuffOp>& ops, 
                                 vector<AugmentedCuffOp>& merged,
-                                bool introns_overwrite_matches)
+                                bool introns_overwrite_matches,
+                                bool allow_flank_introns)
 {	
 #if DEBUG
 	//assert(std::adjacent_find(ops.begin(), ops.end(), g_left_lt) == ops.end());
@@ -356,12 +369,13 @@ void AugmentedCuffOp::merge_ops(const vector<AugmentedCuffOp>& ops,
     }
     if (gaurd_assembly() && merged_length > (int)max_gene_length)
 	{
-		fprintf(stderr, "Warning: nonsense gene merge - product > max_gene_length\n");
+		fprintf(stderr, "Error: nonsense gene merge - product > max_gene_length\n");
 		exit(1);
 	}
 	
 	unknowns.push_back(AugmentedCuffOp(CUFF_UNKNOWN, g_min, g_max - g_min + 1));
 	
+    vector<AugmentedCuffOp> pre_merge = matches;
 	
 	disjoint_ops(matches);
 	disjoint_ops(introns);
@@ -371,37 +385,39 @@ void AugmentedCuffOp::merge_ops(const vector<AugmentedCuffOp>& ops,
 	if (introns_overwrite_matches)
 	{
 		merged = introns;
-		fill_interstices(merged, matches, true);
+		fill_interstices(merged, matches, true, allow_flank_introns);
 		vector<pair<int, int> > gaps;
 		record_gaps(merged, gaps);
 		if (!gaps.empty())
-			fill_interstices(merged, unknowns, false); 
+			fill_interstices(merged, unknowns, false, allow_flank_introns); 
 	}
 	else
 	{
 		merged = matches;
-		fill_interstices(merged, introns, false);
+		fill_interstices(merged, introns, false, allow_flank_introns);
 		vector<pair<int, int> > gaps;
 		record_gaps(merged, gaps);
 		if (!gaps.empty())
-			fill_interstices(merged, unknowns, false); 
+			fill_interstices(merged, unknowns, false, allow_flank_introns); 
 	}
 
-    //FIXME: put these back
-//    assert (merged.front().opcode == CUFF_MATCH);
-//    assert (merged.back().opcode == CUFF_MATCH);
-//    
-//    for (size_t i = 1; i < merged.size(); ++i)
-//    {
-//        if (merged[i].opcode == CUFF_INTRON)
-//        {
-//            assert (merged[i-1].opcode == CUFF_MATCH);
-//        }
-//        else if (merged[i].opcode == CUFF_UNKNOWN)
-//        {
-//            assert (merged[i-1].opcode != CUFF_INTRON);
-//        }
-//    }
+    if (!allow_flank_introns)
+    {
+        assert (merged.front().opcode == CUFF_MATCH);
+        assert (merged.back().opcode == CUFF_MATCH);
+    }
+    for (size_t i = 1; i < merged.size(); ++i)
+    {
+		assert(merged[i-1].g_right() == merged[i].g_left());
+        if (merged[i].opcode == CUFF_INTRON)
+        {
+            assert (merged[i-1].opcode == CUFF_MATCH);
+        }
+        else if (merged[i].opcode == CUFF_UNKNOWN)
+        {
+            assert (merged[i-1].opcode != CUFF_INTRON);
+        }
+    }
 }
     
 
@@ -437,7 +453,7 @@ bool check_merge_length(const vector<AugmentedCuffOp>& ops)
 		}
 
 inline bool has_intron(const Scaffold& scaff)
-			{
+{
 			
 	const vector<AugmentedCuffOp>& ops = scaff.augmented_ops();
 	for (size_t j = 0; j < ops.size(); ++j)
@@ -462,6 +478,219 @@ inline bool has_intron(const Scaffold& scaff)
 //	return false;
 //}
 
+void Scaffold::extend_5(const Scaffold& other)
+{
+	assert(Scaffold::compatible(*this, other, ref_merge_overhang_tolerance));
+	
+	if (strand() == CUFF_FWD)
+	{
+		AugmentedCuffOp& my_first_op = _augmented_ops.front();
+		const AugmentedCuffOp& other_first_op = other.augmented_ops().front();
+
+		my_first_op.g_left(other_first_op.g_left());
+	}
+	else if (strand() == CUFF_REV)
+	{
+		AugmentedCuffOp& my_last_op = _augmented_ops.back();
+		const AugmentedCuffOp& other_last_op = other.augmented_ops().back();
+		
+		my_last_op.g_right(other_last_op.g_right());
+	}
+	else 
+	{
+		assert(false);
+	}
+	
+	int id_length = annotated_trans_id().length();
+	if (id_length < 4 || _annotated_trans_id.substr(id_length-4)!="_ext")
+		_annotated_trans_id += "_ext";
+}
+
+// Clip final 3' exon by given amount
+void Scaffold::trim_3(int to_remove)
+{
+	if(strand() == CUFF_FWD)
+	{
+		AugmentedCuffOp& exon_3 = _augmented_ops.back();
+		assert (exon_3.genomic_length > to_remove);
+		exon_3.genomic_length -= to_remove;
+	}
+	else if(strand() == CUFF_REV)
+	{
+		AugmentedCuffOp& exon_3 = _augmented_ops.front();
+		assert (exon_3.genomic_length > to_remove);
+		exon_3.genomic_offset += to_remove;
+		exon_3.genomic_length -= to_remove;
+
+	}
+	else
+	{
+		assert(false);
+	}
+}
+
+// Clip final 3' exon by given amount
+void Scaffold::extend_3(int to_add)
+{
+	if(strand() == CUFF_FWD)
+	{
+		AugmentedCuffOp& exon_3 = _augmented_ops.back();
+		//assert (exon_3.genomic_length > to_remove);
+		exon_3.genomic_length += to_add;
+	}
+	else if(strand() == CUFF_REV)
+	{
+		AugmentedCuffOp& exon_3 = _augmented_ops.front();
+		//assert (exon_3.genomic_length > to_remove);
+		exon_3.genomic_offset -= to_add;
+		exon_3.genomic_length += to_add;
+        
+	}
+	else
+	{
+		assert(false);
+	}
+}
+
+void Scaffold::tile_with_scaffs(vector<Scaffold>& tile_scaffs, int max_len, int tile_offset) const
+{
+    int min_len = tile_offset;
+	assert(min_len % tile_offset == 0 && max_len % tile_offset == 0);
+	
+	if (length() < max_len)
+		return;
+	
+	size_t l = 0;
+	size_t r = 0;
+	int l_off = 0;
+	int r_off = min_len; // One past end
+	int curr_len = min_len;
+	int remaining_len = length();
+	
+    const vector<AugmentedCuffOp>& orig_ops = augmented_ops();
+    
+	while(true)
+	{
+		while (l < orig_ops.size() && (orig_ops[l].opcode != CUFF_MATCH || l_off >= orig_ops[l].genomic_length))
+		{
+			if (orig_ops[l].opcode == CUFF_MATCH)
+				l_off -= orig_ops[l].genomic_length;
+            ++l;
+			//if(++l == augmented_ops().size())
+			//	assert(false);
+		}
+		while (r < orig_ops.size() && (orig_ops[r].opcode != CUFF_MATCH || r_off > orig_ops[r].genomic_length)) // Strictly > because r_off is one past
+		{
+			if (orig_ops[r].opcode == CUFF_MATCH)
+				r_off -= orig_ops[r].genomic_length;
+            ++r;
+			//if(++r == augmented_ops().size())
+			//	assert(false);
+		}
+		
+		vector<AugmentedCuffOp> ops;
+		
+        //if (l >= orig_ops.size() && r >= orig_ops.size())
+        //    break;
+        
+		if (l==r)
+		{
+            assert (l < orig_ops.size());
+			ops.push_back(AugmentedCuffOp(CUFF_MATCH, orig_ops[l].g_left() + l_off, r_off - l_off));
+		}
+		else
+		{
+            assert(orig_ops.size());
+            //assert(orig_ops[l].genomic_offset != 0);
+            
+			ops.push_back(AugmentedCuffOp(CUFF_MATCH, orig_ops[l].g_left() + l_off, orig_ops[l].genomic_length - l_off));
+			assert(ops.back().g_right() > ops.back().g_left());
+			for(size_t i = l+1; i < r; i++)
+			{
+				ops.push_back(orig_ops[i]);
+			}
+			if (r_off > 0 && r < orig_ops.size())
+            {
+                assert (r < orig_ops.size());
+                //assert (orig_ops[r].genomic_offset != 0);
+				ops.push_back(AugmentedCuffOp(CUFF_MATCH, orig_ops[r].g_left(), r_off));
+            }
+		}
+		assert(ops.back().g_right() > ops.back().g_left());
+
+        // genomic_offset actually could be zero - from an exon starting at coord
+        // 1 in some chromosome of the ref.
+//		foreach(const AugmentedCuffOp& op, ops)
+//        {
+//            assert (op.genomic_offset != 0);
+//        }
+        
+		tile_scaffs.push_back(Scaffold(this->ref_id(), this->strand(), ops, true)); 
+		assert(tile_scaffs.back().length() == curr_len );
+		assert(tile_scaffs.back().left() >= left() &&
+			   tile_scaffs.back().right() <= right());
+		
+		if (l==0 && l_off == 0 && curr_len < max_len) // On left end, not yet full length
+		{
+			curr_len += tile_offset;
+			r_off += tile_offset;
+		}
+		else if(remaining_len - tile_offset < max_len) // On the right end of transcript, decreasing in length
+		{
+			curr_len += augmented_ops().back().genomic_length - r_off - tile_offset; 
+			r_off = augmented_ops().back().genomic_length;
+			l_off += tile_offset;
+			remaining_len -= tile_offset;
+			if (curr_len < min_len)
+				return;
+		}
+		else // In the middle of the transcript, full length
+		{
+			l_off += tile_offset;
+			r_off += tile_offset;
+			remaining_len -= tile_offset;
+		}
+			
+	}
+	
+}
+
+bool Scaffold::sub_scaffold(Scaffold& sub_scaff, int g_left, int match_length) const	
+{
+	size_t i;
+	for(i = 0; i < augmented_ops().size(); ++i)
+	{
+		if (augmented_ops()[i].g_right() > g_left)
+			break;
+	}
+	const AugmentedCuffOp& l_op = augmented_ops()[i++];
+	
+	assert(l_op.opcode == CUFF_MATCH);
+	
+	vector<AugmentedCuffOp> sub_ops;
+	sub_ops.push_back(AugmentedCuffOp(CUFF_MATCH, g_left, min(match_length, l_op.g_right()-g_left)));					 
+	int len_so_far = sub_ops.back().genomic_length;
+						 
+	while(len_so_far < match_length && i < augmented_ops().size())
+	{
+		const AugmentedCuffOp& op = augmented_ops()[i++];
+		if(op.opcode==CUFF_MATCH)
+		{
+			sub_ops.push_back(AugmentedCuffOp(CUFF_MATCH, op.g_left(), min(match_length-len_so_far, op.genomic_length)));
+			len_so_far += sub_ops.back().genomic_length;
+			if (len_so_far >= match_length)
+				break;
+		}
+		else 
+		{
+			sub_ops.push_back(op);
+		}
+	}
+	
+	//assert(len_so_far == match_length);
+	sub_scaff = Scaffold(this->ref_id(), this->strand(), sub_ops, true);
+	return (len_so_far == match_length);
+}
 
 void Scaffold::merge(const Scaffold& lhs, 
 					 const Scaffold& rhs, 
@@ -496,14 +725,12 @@ void Scaffold::merge(const Scaffold& lhs,
 													  merged._mates_in_scaff.end());
 	merged._mates_in_scaff.erase(new_end, merged._mates_in_scaff.end());
 	
-	merged._right = merged._augmented_ops.back().g_right();
-	merged._left = merged._augmented_ops.front().g_left();
 	
-	int r_check = merged._left;
+	int r_check = merged.left();
 	for (size_t i = 0; i < merged._augmented_ops.size(); ++i)
 		r_check += merged._augmented_ops[i].genomic_length;
 	
-	assert(r_check == merged._right);
+	assert(r_check == merged.right());
 	
 	if (lhs.strand() != CUFF_STRAND_UNKNOWN)
 		merged._strand = lhs.strand();
@@ -511,7 +738,15 @@ void Scaffold::merge(const Scaffold& lhs,
 		merged._strand = rhs.strand();
 	
 	merged._has_intron = has_intron(merged);
-	assert(!merged.has_intron() || merged._strand != CUFF_STRAND_UNKNOWN);
+	assert(merged._strand != CUFF_STRAND_UNKNOWN || !merged.has_intron() );
+	assert(!merged.is_ref());
+    
+    if (library_type == "transfrags")
+    {
+        double avg_fpkm = lhs.fpkm() + rhs.fpkm();
+        avg_fpkm /= 2;
+        merged.fpkm(avg_fpkm);
+    }
 }
 
 
@@ -527,7 +762,6 @@ void Scaffold::merge(const vector<Scaffold>& s,
 	for (size_t i = 0; i < s.size(); ++i)
 	{
 		ops.insert(ops.end(), s[i]._augmented_ops.begin(), s[i]._augmented_ops.end());
-		
 		merged._mates_in_scaff.insert(merged._mates_in_scaff.end(),
 									  s[i]._mates_in_scaff.begin(), 
 									  s[i]._mates_in_scaff.end());
@@ -552,6 +786,7 @@ void Scaffold::merge(const vector<Scaffold>& s,
 	
 	AugmentedCuffOp::merge_ops(ops, merged._augmented_ops, introns_overwrite_matches);
 	
+	
 	//assert (ops.empty() || !(merged._augmented_ops.empty()));
 #ifdef DEBUG
 	if (merged._augmented_ops.empty() || 
@@ -564,25 +799,33 @@ void Scaffold::merge(const vector<Scaffold>& s,
 	
 	merged._ref_id = s.front().ref_id();
 	merged._strand = strand;
-	
-	merged._left = merged._augmented_ops.front().g_left();
-	merged._right = merged._augmented_ops.back().g_right();
-	
-	int r_check = merged._left;
+		
+	int r_check = merged.left();
 	for (size_t i = 0; i < merged._augmented_ops.size(); ++i)
 		r_check += merged._augmented_ops[i].genomic_length;
 	
 #ifdef DEBUG
-	if (r_check != merged._right)
+	if (r_check != merged.right())
 	{
 		AugmentedCuffOp::merge_ops(ops, merged._augmented_ops, introns_overwrite_matches);
 	}
 #endif
 	
-	assert (r_check == merged._right);
+	assert (r_check == merged.right());
 	merged._has_intron = has_intron(merged);
-	
-	assert(!merged.has_intron()|| merged._strand != CUFF_STRAND_UNKNOWN);
+
+	assert(merged._strand != CUFF_STRAND_UNKNOWN || !merged.has_strand_support());
+    
+    if (library_type == "transfrags")
+    {
+        double avg_fpkm = 0.0;
+        foreach (const Scaffold& sc, s)
+        {
+            avg_fpkm += sc.fpkm();
+        }
+        avg_fpkm /= s.size();
+        merged.fpkm(avg_fpkm);
+    }
 }
 
 void Scaffold::fill_gaps(int filled_gap_size)
@@ -609,6 +852,8 @@ void Scaffold::fill_gaps(int filled_gap_size)
 	
 	AugmentedCuffOp::merge_ops(ops, _augmented_ops, false);
 	_has_intron = has_intron(*this);
+	assert(!has_strand_support() || _strand != CUFF_STRAND_UNKNOWN);
+
 }
 
 void Scaffold::fill_gaps(const vector<AugmentedCuffOp>& filler)
@@ -617,24 +862,77 @@ void Scaffold::fill_gaps(const vector<AugmentedCuffOp>& filler)
 	
 	const vector<AugmentedCuffOp>& orig_ops = augmented_ops();
     
-	for (size_t i = 0; i < orig_ops.size(); ++i)
-	{
-		if (orig_ops[i].opcode == CUFF_UNKNOWN)
-		{
-            
-		}
-		else
-		{
-			ops.push_back(orig_ops[i]);
-		}
+	const vector<AugmentedCuffOp> og_ops = augmented_ops();
+	
+    vector<AugmentedCuffOp> unknowns;
+	
+    size_t g_max = 0;
+	size_t g_min = 0xFFFFFFFF;
+    
+    vector<AugmentedCuffOp> tmp_filler = filler;
+    
+    foreach(const AugmentedCuffOp& op, orig_ops)
+    {
+		assert (op.g_left() < op.g_right());
+		
+		if ((size_t)op.g_left() < g_min)
+			g_min = op.g_left();
+		if ((size_t)op.g_right() > g_max)
+			g_max = op.g_right();
 	}
+
+	tmp_filler.push_back(AugmentedCuffOp(CUFF_UNKNOWN, g_min, g_max - g_min + 1));
+    sort(tmp_filler.begin(), tmp_filler.end(), AugmentedCuffOp::g_left_lt);
+    
+    vector<AugmentedCuffOp> padded_filler;
+    AugmentedCuffOp::merge_ops(tmp_filler, padded_filler, false);
+    	
+    vector<AugmentedCuffOp> overlapping;
+    foreach (const AugmentedCuffOp& op, padded_filler)
+    {
+		//if (left() <= op.g_left() && right() >= op.g_right()
+		if(::overlap_in_genome(op.g_left(),op.g_right(), left(), right())
+            && (op.opcode != CUFF_UNKNOWN || !overlapping.empty()))
+        {
+            overlapping.push_back(op);
+        }
+    }
     
-    AugmentedCuffOp::fill_interstices(ops, filler, false); 
+    overlapping.insert(overlapping.end(), _augmented_ops.begin(),_augmented_ops.end()); 
+    sort(overlapping.begin(), overlapping.end(), AugmentedCuffOp::g_left_lt);
 	
-	sort(ops.begin(), ops.end(), AugmentedCuffOp::g_left_lt);
+	// Trim first in last in case they hang over the scaffold boundaries
+	if (overlapping.front().g_left() < left())
+	{
+		AugmentedCuffOp& first_op = overlapping.front();
+		first_op.genomic_length -= (left() - first_op.g_left());
+		first_op.genomic_offset = left();
+	}
+	if (overlapping.back().g_right() > right())
+	{
+		AugmentedCuffOp& last_op = overlapping.back();
+		last_op.genomic_length -= (last_op.g_right() - right());
+	}
+	if (overlapping.back().opcode == CUFF_INTRON && overlapping.back().genomic_length <= bowtie_overhang_tolerance)
+	{
+		overlapping.pop_back();
+	}
+	if (overlapping.front().opcode == CUFF_INTRON && overlapping.front().genomic_length <= bowtie_overhang_tolerance)
+	{
+		overlapping.erase(overlapping.begin(), overlapping.begin()+1);
+	}
 	
-	AugmentedCuffOp::merge_ops(ops, _augmented_ops, false);
+    // we don't want either the terminal ops in the filler to be unknowns,
+    // because they'll just propogate to the scaffold we're trying to fill.
+    if (!overlapping.empty() && overlapping.back().opcode == CUFF_UNKNOWN)
+        overlapping.pop_back();
+    
+    if (overlapping.empty())
+        return;
+    
+	AugmentedCuffOp::merge_ops(overlapping, _augmented_ops, true);
 	_has_intron = has_intron(*this);
+	assert(!has_strand_support() || _strand != CUFF_STRAND_UNKNOWN);
 }
 
 bool Scaffold::overlap_in_genome(const Scaffold& lhs, 
@@ -672,9 +970,32 @@ bool Scaffold::strand_agree(const Scaffold& lhs,
 	return strand;
 }
 
-	
+bool Scaffold::exons_overlap(const Scaffold& lhs, 
+							 const Scaffold& rhs)
+{
+	const vector<AugmentedCuffOp>& lhs_ops = lhs.augmented_ops();
+	const vector<AugmentedCuffOp>& rhs_ops = rhs.augmented_ops();
+
+	for (size_t l = 0; l < lhs_ops.size(); l++)
+	{
+		if (lhs_ops[l].opcode != CUFF_MATCH)
+			continue;
+		
+		for (size_t r = 0; r < rhs_ops.size(); r++)
+		{
+			if (rhs_ops[r].opcode == CUFF_MATCH &&
+				AugmentedCuffOp::overlap_in_genome(lhs_ops[l], rhs_ops[r]))
+			{
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
 bool Scaffold::compatible(const Scaffold& lhs, 
-						  const Scaffold& rhs)
+						  const Scaffold& rhs,
+						  int overhang_tolerance)
 {	
 	if (!strand_agree(lhs, rhs))
 		return false;
@@ -684,7 +1005,7 @@ bool Scaffold::compatible(const Scaffold& lhs,
 		if (overlap_in_genome(lhs, rhs, olap_radius))
 		{
 			// check compatibility
-			if (!compatible_contigs(lhs, rhs))
+			if (!compatible_contigs(lhs, rhs, overhang_tolerance))
 				return false;
 		}
 	}
@@ -693,7 +1014,7 @@ bool Scaffold::compatible(const Scaffold& lhs,
 		if (overlap_in_genome(rhs, lhs, olap_radius))
 		{
 			// check compatibility
-			if (!compatible_contigs(rhs, lhs))
+			if (!compatible_contigs(rhs, lhs, overhang_tolerance))
 				return false;
 		}
 	}
@@ -702,8 +1023,9 @@ bool Scaffold::compatible(const Scaffold& lhs,
 	return true;
 }
 
-bool Scaffold::distance_compatible_contigs(const Scaffold& lhs, 
-										   const Scaffold& rhs)
+bool Scaffold::compatible_contigs(const Scaffold& lhs, 
+								  const Scaffold& rhs,
+								  int overhang_tolerance)
 {
 	const vector<AugmentedCuffOp>& l_aug = lhs._augmented_ops;
 	const vector<AugmentedCuffOp>& r_aug = rhs._augmented_ops;
@@ -722,7 +1044,7 @@ bool Scaffold::distance_compatible_contigs(const Scaffold& lhs,
 			if (AugmentedCuffOp::overlap_in_genome(l_op, r_op))
 			{
 				// check compatibility
-				if (!AugmentedCuffOp::compatible(l_op, r_op))
+				if (!AugmentedCuffOp::compatible(l_op, r_op, overhang_tolerance))
 					return false;
 //				if (l_op.opcode == CUFF_UNKNOWN && 
 //					r_op.opcode == CUFF_MATCH)
@@ -747,7 +1069,7 @@ bool Scaffold::distance_compatible_contigs(const Scaffold& lhs,
 			if (AugmentedCuffOp::overlap_in_genome(r_op, l_op))
 			{
 				// check compatibility
-				if (!AugmentedCuffOp::compatible(r_op, l_op))
+				if (!AugmentedCuffOp::compatible(r_op, l_op, overhang_tolerance))
 						return false;
 //				if (r_op.opcode == CUFF_UNKNOWN && 
 //					l_op.opcode == CUFF_MATCH)
@@ -1112,19 +1434,38 @@ pair <int,int> Scaffold::genomic_to_transcript_span(pair<int,int> g_span) const
 	return make_pair(s_start, s_end);
 }
 
-// Returns true only when both the start and end are found
+// Returns true only when both the start and end are found (ie, frag_len is known), which
+// can only happen if the read is paired.  Returned values that are equal to the trans_len 
+// should be ignored, as they are invalid.
 // We can't use EmpDist unless it is unpaired since we call this function in inspect_bundle
+// End is inclusive
+// Returned indices are oriented with the transript!
 bool Scaffold::map_frag(const MateHit& hit, int& start, int& end, int& frag_len) const
 {
 	
 	int trans_len = length();
-	
-	// Defaults will cause them to be ignored when they are unknown
+    
+//    if (Scaffold(hit).augmented_ops() == augmented_ops())
+//    {
+//        int a = 4;
+//    }
+    
 	start = trans_len;
 	end = trans_len;
 	
-	
-	if (hit.is_pair())
+    Scaffold h(hit);
+    if(!(contains(h) && Scaffold::compatible(*this, h)))
+       return false;
+    
+    if (hit.read_group_props()->complete_fragments())
+    {
+        pair<int,int> g_span = make_pair(hit.left(), hit.right() - 1);
+		pair<int,int> t_span = genomic_to_transcript_span(g_span);
+		start = t_span.first;
+		end = t_span.second;
+		frag_len = abs(end-start)+1;
+    }
+	else if (hit.is_pair())
 	{
 		pair<int,int> g_span = hit.genomic_outer_span();
 		pair<int,int> t_span = genomic_to_transcript_span(g_span);
@@ -1141,8 +1482,8 @@ bool Scaffold::map_frag(const MateHit& hit, int& start, int& end, int& frag_len)
 	{
 		shared_ptr<const EmpDist> frag_len_dist = hit.read_group_props()->frag_len_dist();
 
-		if (hit.left_alignment()->antisense_align() && strand() != CUFF_REV 
-			|| !(hit.left_alignment()->antisense_align()) && strand() == CUFF_REV)
+		if ((hit.left_alignment()->antisense_align() && strand() != CUFF_REV) 
+			|| (!hit.left_alignment()->antisense_align() && strand() == CUFF_REV))
 		{
 			int g_end  = (strand()!=CUFF_REV) ? hit.right()-1:hit.left();
 			end = genomic_to_transcript_coord(g_end);
@@ -1205,7 +1546,7 @@ void Scaffold::clear_hits()
 bool Scaffold::add_hit(const MateHit* hit)
 {
 	Scaffold hs(*hit);
-	if (Scaffold::overlap_in_genome(*this, hs, olap_radius) &&
+	if (contains(hs) &&
 		Scaffold::compatible(*this, hs))
 	{
 		if (!binary_search(_mates_in_scaff.begin(),
@@ -1250,12 +1591,12 @@ void Scaffold::get_complete_subscaffolds(vector<Scaffold>& complete)
 				int right_known;
 				
 				if (i == _augmented_ops.size() - 1)
-					right_known = _right;
+					right_known = right();
 				else
 					right_known = op.g_left() - 1;
 				
 				if (last_unknown == -1)
-					left_known = _left;
+					left_known = left();
 				else
 					left_known = _augmented_ops[last_unknown].g_right();
 				
@@ -1275,12 +1616,41 @@ void Scaffold::get_complete_subscaffolds(vector<Scaffold>& complete)
                     {
 
                         const MateHit& m = **mitr;
-                        if (left_known <= m.left() && m.right() < right_known)
+                        if (left_known <= m.left() && m.right() <= right_known)
                         {
                             known.add_hit(&m);
                         }
                     }
                     
+                   // if (known.has_intron())
+                        known.strand(_strand);
+                    
+                    //assert (!known.mate_hits().empty());
+                    //assert(!known.has_intron()|| known.strand() != CUFF_STRAND_UNKNOWN);
+                    
+                   // const vector<const MateHit*>& hits = known.mate_hits();
+                   // bool contains_spliced_hit = false;
+                   // foreach (const MateHit* h, hits)
+                   // {
+                   //     const ReadHit* left = h->left_alignment();
+                   //     const ReadHit* right = h->right_alignment();
+                   //     if (left && left->contains_splice())
+                   //     {
+                   //         contains_spliced_hit = true;
+                   //         break;
+                   //     }
+                   //     if (right && right->contains_splice())
+                   //     {
+                   //         contains_spliced_hit = true;
+                   //         break;
+                   //     }
+				   // }
+                    
+                   // if (!hits.empty() && !contains_spliced_hit && !known.is_ref())
+                   // {
+                   //     known.strand(CUFF_STRAND_UNKNOWN);
+                   // }
+                    
                     complete.push_back(known);
                 }
                     
@@ -1288,10 +1658,129 @@ void Scaffold::get_complete_subscaffolds(vector<Scaffold>& complete)
                 leftmost_known_op = -1;
 			}
 		}
-		
 	}
 }
 
+double Scaffold::internal_exon_coverage() const
+{
+    // First check if there are internal exons
+    if (augmented_ops().size() < 5) 
+        return 0.0;
+    
+    int left = augmented_ops()[2].g_left();
+    int right = augmented_ops()[augmented_ops().size() - 3].g_right();
+    vector<bool> covered(right-left, 0);
+    foreach(const MateHit* h, mate_hits())
+    {
+        if (::overlap_in_genome(h->left(),h->right(), left, right))
+        {
+            for (int i = max(h->left()-left, 0); i < min(h->right()-left, right-left); ++i)
+            {
+                assert(i < covered.size());
+                covered[i] = 1;
+            }
+        }
+    }
+    double percent_covered = accumulate(covered.begin(),covered.end(),0.0)/(right-left);
+    return percent_covered;
+}
+
+bool Scaffold::has_strand_support(vector<shared_ptr<Scaffold> >* ref_scaffs) const
+{
+	if (strand() == CUFF_STRAND_UNKNOWN)
+		return false;
+	
+	// FIXME:  This is not true for non-canonical splicing
+	if (has_intron())
+		return true;
+	
+	foreach (const MateHit* h, mate_hits())
+	{
+		if (h->strand() == strand())
+			return true;
+		assert(h->strand() == CUFF_STRAND_UNKNOWN && !h->contains_splice());
+	}
+	
+	if (ref_scaffs == NULL)
+		return false;
+	
+	foreach (shared_ptr<Scaffold const> ref_scaff, *ref_scaffs)
+	{
+		if (ref_scaff->strand() == strand() && exons_overlap(*this, *ref_scaff))
+			return true;
+	}
+		
+	return false;
+}
+
+bool Scaffold::has_struct_support(set<AugmentedCuffOp>& hit_introns) const
+{
+    if(augmented_ops().size() == 1)
+        return mate_hits().size() > 0;
+
+    if(augmented_ops().size() == 3)
+        return hits_support_introns(hit_introns);
+    
+    return (hits_support_introns(hit_introns) && internal_exon_coverage() == 1.0);
+}
+
+
+bool Scaffold::hits_support_introns() const
+{
+    set<AugmentedCuffOp> hit_introns;
+    set<AugmentedCuffOp> scaffold_introns;
+    foreach(const MateHit* h, _mates_in_scaff)
+    {
+        Scaffold s(*h);
+        foreach (AugmentedCuffOp a, s.augmented_ops())
+        {
+            if (a.opcode == CUFF_INTRON)
+            {
+                hit_introns.insert(a);
+            }
+        }
+    }
+    foreach (AugmentedCuffOp a, _augmented_ops)
+    {
+        if (a.opcode == CUFF_INTRON)
+        {
+            scaffold_introns.insert(a);
+        }
+    }
+    
+    if (hit_introns != scaffold_introns)
+    {
+        fprintf(stderr, "********************\n");
+        foreach(const AugmentedCuffOp& a, hit_introns)
+        {
+            fprintf(stderr, "%d - %d\n", a.g_left(), a.g_right());
+        }
+        
+        fprintf(stderr, "####################\n");    
+        foreach(const AugmentedCuffOp& a, scaffold_introns)
+        {
+            fprintf(stderr, "%d - %d\n", a.g_left(), a.g_right());
+        }
+    }
+    
+    return hit_introns == scaffold_introns;
+}
+
+bool Scaffold::hits_support_introns(set<AugmentedCuffOp>& hit_introns) const
+{
+    set<AugmentedCuffOp> scaffold_introns;
+
+    foreach (AugmentedCuffOp a, _augmented_ops)
+    {
+        if (a.opcode == CUFF_INTRON)
+        {
+            scaffold_introns.insert(a);
+        }
+    }
+    
+    return includes(hit_introns.begin(),hit_introns.end(), scaffold_introns.begin(), scaffold_introns.end());
+}
+
 bool scaff_lt(const Scaffold& lhs, const Scaffold& rhs)
 {
 	return lhs.left() < rhs.left();
diff --git a/src/scaffolds.h b/src/scaffolds.h
index 41b822d..0f29e80 100644
--- a/src/scaffolds.h
+++ b/src/scaffolds.h
@@ -35,7 +35,18 @@ struct AugmentedCuffOp
 		assert (genomic_length >= 0);
 	}
 	
+	void g_left(int left) 
+	{ 
+		int right = genomic_offset + genomic_length;
+		genomic_offset = left;
+		genomic_length = right - left;
+	}
 	int g_left() const { return genomic_offset; }
+	
+	void g_right(int right) 
+	{ 
+		genomic_length = right - genomic_offset;
+	}
 	int g_right() const { return genomic_offset + genomic_length; }
 	
 	static bool overlap_in_genome(const AugmentedCuffOp& lhs,
@@ -95,7 +106,8 @@ struct AugmentedCuffOp
 	}
 	
 	static bool compatible(const AugmentedCuffOp& lhs,
-						   const AugmentedCuffOp& rhs);
+						   const AugmentedCuffOp& rhs,
+						   int overhang_tolerance = bowtie_overhang_tolerance);
 	
 	bool operator==(const AugmentedCuffOp& rhs) const
 	{
@@ -131,11 +143,13 @@ struct AugmentedCuffOp
     
     static void merge_ops(const std::vector<AugmentedCuffOp>& ops, 
 						  vector<AugmentedCuffOp>& merged,
-						  bool introns_overwrite_matches);
+						  bool introns_overwrite_matches,
+                          bool allow_flank_introns = false);
     
     static void fill_interstices(vector<AugmentedCuffOp>& to_fill,
 								 const vector<AugmentedCuffOp>& filler,
-								 bool allow_flank_fill);
+								 bool allow_flank_fill,
+                                 bool allow_flank_introns);
 	
 	CuffOpCode opcode;
 	int genomic_offset;
@@ -172,7 +186,8 @@ class Scaffold
                     //g_left -= cig[i].length;
 					break;
                 case DEL:
-                    ops.back().genomic_length += cig[i].length;
+					if (!ops.empty())
+						ops.back().genomic_length += cig[i].length;
                     g_left += cig[i].length;
 					break;
 				default:
@@ -189,14 +204,15 @@ public:
 	
 	Scaffold() :
 		_ref_id(0), 
+		_is_ref(false),
 		_strand(CUFF_STRAND_UNKNOWN), 
-		_classcode(0) {}
+		_classcode(0),
+        _fpkm(0.0) {}
 	
 	Scaffold(const MateHit& mate) :
-		  _ref_id(mate.ref_id()),
-		  _left(mate.left()), 
-	      _right(mate.right()),
-	      _classcode(0)
+		_ref_id(mate.ref_id()),
+		_is_ref(false),
+	    _classcode(0)
 	{
 		const ReadHit* left_hit = mate.left_alignment();
 		//CuffAlign a;
@@ -243,66 +259,82 @@ public:
 		}
 		_mates_in_scaff.push_back(&mate);
 		sort(aug_ops.begin(), aug_ops.end(), AugmentedCuffOp::g_left_lt);
-        
-        foreach(AugmentedCuffOp& op, aug_ops)
+
+        for(size_t i = 0; i < aug_ops.size(); ++i)
         {
-            assert (op.genomic_length >= 1);
+            assert (aug_ops[i].genomic_length >= 1);
         }
         
         AugmentedCuffOp::merge_ops(aug_ops, _augmented_ops, false);
-        
-		_right = _augmented_ops.back().g_right();
-		_left = _augmented_ops.front().g_left();
 		
-		
-		int r_check = _left;
+		int r_check = left();
 		for (size_t i = 0; i < _augmented_ops.size(); ++i)
 			r_check += _augmented_ops[i].genomic_length;
 		
 #ifdef DEBUG
-		if (r_check != _right)
+		if (r_check != right())
 		{
             AugmentedCuffOp::merge_ops(aug_ops, _augmented_ops, false);
 		}
 #endif
-		assert (r_check == _right);
+		assert (r_check == right());
 		
 		_has_intron = has_intron(*this);
 		
-		//FIXME: reinstate this assert after fixing the Graveley bug.
-		assert(!_has_intron || _strand != CUFF_STRAND_UNKNOWN);
+		assert(!has_strand_support() || _strand != CUFF_STRAND_UNKNOWN);
 
-        assert (_augmented_ops.front().opcode == CUFF_MATCH);
+        for(size_t i = 1; i < _augmented_ops.size(); ++i)
+		{
+			assert(_augmented_ops[i-1].g_right() == _augmented_ops[i].g_left());
+		}
+		
+		assert (_augmented_ops.front().opcode == CUFF_MATCH);
         assert (_augmented_ops.back().opcode == CUFF_MATCH);
+        
+        if (library_type == "transfrags")
+        {
+            double avg_fpkm = mate.mass();
+            fpkm(avg_fpkm);
+        }
 	}
 	
 	Scaffold(const vector<Scaffold>& hits, bool introns_overwrite_matches = true) 
-		:  _classcode(0)
+		:  _is_ref(false), _classcode(0)
 	{
 		assert (!hits.empty());
 		_ref_id = hits[0].ref_id();
 		
 		Scaffold::merge(hits, *this, introns_overwrite_matches);
 		
-		assert(!_has_intron || _strand != CUFF_STRAND_UNKNOWN);
+		assert(!has_strand_support() || _strand != CUFF_STRAND_UNKNOWN);
         
         assert (_augmented_ops.front().opcode == CUFF_MATCH);
         assert (_augmented_ops.back().opcode == CUFF_MATCH);
+        
+        if (library_type == "transfrags")
+        {
+            double avg_fpkm = 0.0;
+            foreach (const Scaffold& sc, hits)
+            {
+                avg_fpkm += sc.fpkm();
+            }
+            avg_fpkm /= hits.size();
+            fpkm(avg_fpkm);
+        }
 	}
 	
 	// For manually constructing scaffolds, for example when a reference is 
 	// available
-	Scaffold(RefID ref_id, CuffStrand strand, const vector<AugmentedCuffOp>& ops)
+	Scaffold(RefID ref_id, CuffStrand strand, const vector<AugmentedCuffOp>& ops, bool is_ref = false)
 	: _ref_id(ref_id), 
 	  _augmented_ops(ops), 
 	  _strand(strand),
 	  _classcode(0)
 	{
-		_right = _augmented_ops.back().g_right();
-		_left = _augmented_ops.front().g_left();
 		_has_intron = has_intron(*this);
+		_is_ref = is_ref;
 		
-		assert(!_has_intron || _strand != CUFF_STRAND_UNKNOWN);
+		assert(!has_strand_support() || _strand != CUFF_STRAND_UNKNOWN);
 
         assert (_augmented_ops.front().opcode == CUFF_MATCH);
         assert (_augmented_ops.back().opcode == CUFF_MATCH);
@@ -310,9 +342,13 @@ public:
 	
 	//int get_id() const { return _id; }
 		
-	int left() const { return _left; }
+	int left() const { return _augmented_ops.front().g_left(); }
+	int right() const  { return _augmented_ops.back().g_right(); }
 	
-	int right() const  { return _right; }
+	const vector<const MateHit*>& mate_hits() const { return _mates_in_scaff; }
+	
+	RefID ref_id() const { return _ref_id; }
+	void ref_id(RefID rid) { _ref_id = rid; }
 	
 	const string& annotated_trans_id() const { return _annotated_trans_id; }
 	void annotated_trans_id(const string& ann_name) { _annotated_trans_id = ann_name; }
@@ -353,23 +389,51 @@ public:
 		return -1.0;
 	}
 	
-			
 	char nearest_ref_classcode() const { return _classcode; }
 	void nearest_ref_classcode(char cc) { _classcode = cc; }
 	
 	bool has_intron() const { return _has_intron; }
 	bool has_suspicious_unknown() const { return has_suspicious_unknown(*this); }
+
+    // returns the fraction coverage of internal exons, returns 0 if no internal exons
+	double internal_exon_coverage() const;
+    
+    // returns true if the scaffold strand is supported with reads or exon overlap with
+    // a reference scaffold of known strand (since the scaffold may have been created with faux reads)
+    bool has_strand_support(vector<shared_ptr<Scaffold> >* ref_scaffs = NULL) const;
+    
+    // returns true if all introns are supported with splice reads, false ow
+    bool hits_support_introns() const; 
+    bool hits_support_introns(set<AugmentedCuffOp>& hit_introns) const; 
+
+    // returns true if all internal exons are fully covered and hits support introns, false ow
+    bool has_struct_support(set<AugmentedCuffOp>& hit_introns) const;
+    
+	bool is_ref() const { return _is_ref; }
+	void is_ref(bool ir) { _is_ref = ir; }
 	
-	CuffStrand strand() const { return _strand; }
-	void strand(CuffStrand strand) { _strand = strand; }
+	CuffStrand strand() const 
+    { 
+        return _strand; 
+    }
+    
+	void strand(CuffStrand strand) 
+    { 
+		assert(!has_strand_support() || _strand != CUFF_STRAND_UNKNOWN);
+        _strand = strand; 
+    }
 	
 	// Could we merge lhs and rhs together?
 	static bool compatible(const Scaffold& lhs, 
-						   const Scaffold& rhs);
+						   const Scaffold& rhs,
+						   int overhang_tolerance = bowtie_overhang_tolerance);
 	
 	static bool strand_agree(const Scaffold& lhs, 
 							 const Scaffold& rhs);
 	
+	static bool exons_overlap(const Scaffold& lhs,
+							  const Scaffold& rhs);
+	
 	// Incorporate Scaffold chow into this one.
 	static void merge(const Scaffold& lhs, 
 					  const Scaffold& rhs, 
@@ -380,12 +444,71 @@ public:
 					  Scaffold& merged,
 					  bool introns_overwrite_matches);
 
-	const vector<const MateHit*>& mate_hits() const { return _mates_in_scaff; }
-	RefID ref_id() const { return _ref_id; }
+	// Extend 5' end using beginning of other scaffold without adding new exons.
+	void extend_5(const Scaffold& other);
+	// Clip final 3' exon by given amount
+	void trim_3(int to_remove);
+    
+    // Extend final 3' exon by given amount
+    void extend_3(int to_add);
+
+	void tile_with_scaffs(vector<Scaffold>& tile_scaffs, int max_len, int tile_offset) const;
+
+	// Creates a scaffold that matches this one but only covers the section from g_left for
+	// a distance of match_length.  It is assumed that this region is contained in the scaffold.
+	// sub_scaff should be an empty Scaffold object.
+	bool sub_scaffold(Scaffold& sub_scaff, int g_left, int match_length) const;
+
+ 	// Tests whether the other scaffold is contained allowing the given overhang
+	bool contains(const Scaffold& other, int ohang_5 = 0, int ohang_3 = 0) const
+	{
+		if (left() <= other.left() && right()>= other.right())
+			return true;
+		
+        if (!(ohang_5 || ohang_3))
+            return false;
+        
+		int left_hang;
+		int right_hang;
+		switch(strand())
+		{
+			case CUFF_FWD:
+				left_hang = ohang_5;
+				right_hang = ohang_3;
+				break;
+			case CUFF_REV:
+				left_hang = ohang_3;
+				right_hang = ohang_5;
+				break;
+			default:
+				left_hang = max(ohang_3, ohang_5);
+				right_hang = left_hang;
+		}
+				
+		// Test to see if it is contained within the relaxed boundaries
+		if ((left()-left_hang) <= other.left() && (right() + right_hang) >= other.right())
+		{
+			// Ensure that there are no exons outside of the strict boundaries
+			return (other.augmented_ops().front().g_right() > left() && other.augmented_ops().back().g_left() < right()); 
+		}
+		
+		return false;
+
+	}
 	
-	bool contains(const Scaffold& other) const
+	// Tests whether the other scaffold contains the 5' end and is contained (allowing some overhang) on the 3' end
+	// There can be no additional exons on the 5' end
+	bool overlapped_3(const Scaffold& other, int ohang_5 = 0, int ohang_3 = 0) const
 	{
-		return (left() <= other.left() && right() >= other.right());
+		switch(strand())
+		{
+			case CUFF_FWD:
+				return((left() + ohang_5 >= other.left() && right() + ohang_3 >= other.right()) && other.augmented_ops().front().g_right() > left());
+			case CUFF_REV:
+				return ((right() - ohang_5 <= other.right() && left() - ohang_3 <= other.left()) && other.augmented_ops().back().g_left() < right());
+			default:
+				return false;
+		}
 	}
 	
 	int match_length(int left, int right) const;
@@ -431,6 +554,8 @@ public:
 						  const AugmentedCuffOp& rhs);
 	
 	const vector<AugmentedCuffOp>& augmented_ops() const { return _augmented_ops; }
+	void augmented_ops(vector<AugmentedCuffOp> aug_ops) { _augmented_ops = aug_ops; }
+
 	
 	static bool overlap_in_genome(const Scaffold& lhs, 
 								  const Scaffold& rhs, 
@@ -472,7 +597,6 @@ public:
 	bool add_hit(const MateHit*);
 	
 	void get_complete_subscaffolds(vector<Scaffold>& complete);
-    
 private: 
 	
 	void initialize_exon_lists();
@@ -514,17 +638,10 @@ private:
 	static double min_score(const Scaffold& contig, 
 							const vector<pair<int, int> >& inners);
 	
-	static bool distance_compatible_contigs(const Scaffold& lhs, 
-											const Scaffold& rhs);
-	
 	static bool compatible_contigs(const Scaffold& lhs, 
-								   const Scaffold& rhs)
-	{
-		
-		bool dist = distance_compatible_contigs(lhs,
-												rhs);
-		return (dist);
-	}
+											const Scaffold& rhs,
+											int overhang_tolerance = bowtie_overhang_tolerance);
+	
 	
 	typedef vector<AugmentedCuffOp> OpList;
 	
@@ -540,9 +657,8 @@ private:
 	
 	vector<const MateHit*> _mates_in_scaff;
 	
-	int _left;
-	int _right;
 	bool _has_intron; 
+	bool _is_ref;
 	
 	vector<AugmentedCuffOp> _augmented_ops;
 	CuffStrand _strand;
diff --git a/src/update_check.h b/src/update_check.h
new file mode 100644
index 0000000..703c2f8
--- /dev/null
+++ b/src/update_check.h
@@ -0,0 +1,112 @@
+/*
+ *  update_check.h
+ *  cufflinks
+ *  Based on code from http://www.linuxhowtos.org/C_C++/socket.htm
+ *  Modified by Adam Roberts on 1/18/11.
+ *
+ */
+
+#include <signal.h>
+#include <strings.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h> 
+
+int NUM_SEPS = 3;
+int CONNECT_TIMEOUT = 5;
+
+static int sTimeout = 0; 
+
+static void AlarmHandler(int sig) 
+{ 
+	sTimeout = 1; 
+} 
+
+bool error(const char *msg)
+{
+	return false;
+}
+
+int parse_version_str(char* version_str)
+{
+	int version_int = 0;
+	char* token = strtok(version_str,".");
+    for(int i = 0; i < NUM_SEPS; ++i)
+    {
+		version_int += atoi(token)*pow(100.,NUM_SEPS-i);
+	}
+	return version_int;
+}
+
+bool get_current_version(char* curr_version)
+{
+    int sockfd, portno, n;
+    struct sockaddr_in serv_addr;
+    struct hostent *server;
+	
+    portno = 80;
+    sockfd = socket(AF_INET, SOCK_STREAM, 0);
+    if (sockfd < 0) 
+        return error("ERROR opening socket");
+	
+    server = gethostbyname("cufflinks.cbcb.umd.edu");
+    if (server == NULL) 
+        return error("ERROR, no such host");
+
+    bzero((char *) &serv_addr, sizeof(serv_addr));
+    serv_addr.sin_family = AF_INET;
+    bcopy((char *)server->h_addr, 
+		  (char *)&serv_addr.sin_addr.s_addr,
+		  server->h_length);
+    serv_addr.sin_port = htons(portno);
+    
+	signal(SIGALRM, AlarmHandler); 
+	sTimeout = 0; 
+	alarm(CONNECT_TIMEOUT); 
+	
+	int ret;
+	ret = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
+	if (ret < 0 || sTimeout)
+	{
+		return error("ERROR connecting");
+	}
+	
+	char buffer[1024];
+	strcpy(buffer, "GET /curr_cuff_version HTTP/1.1\nHost: cufflinks.cbcb.umd.edu\n\n");
+	n = write(sockfd,buffer,1024);
+	
+    if (n < 0) 
+		return error("ERROR writing to socket");
+	bzero(curr_version, sizeof(curr_version));
+    n = read(sockfd,buffer,1024);
+    if (n < 0) 
+		return error("ERROR reading from socket");
+
+	char* token;
+	token = strtok(buffer, "$");
+	token = strtok(NULL, "$");
+	if (token==NULL)
+		return error("ERROR parsing response");
+	
+	strcpy(curr_version, token);
+		
+	return true;
+}
+
+void check_version(const char* this_version)
+{
+	char curr_version[256];
+    memset(curr_version, 0, sizeof(curr_version));
+	if (get_current_version(curr_version))
+	{
+		if (strcmp(curr_version, this_version)==0)
+			fprintf(stderr, "You are using Cufflinks v%s, which is the most recent release.\n", PACKAGE_VERSION);
+		else
+			fprintf(stderr, "Warning: Your version of Cufflinks is not up-to-date. It is recommended that you upgrade to Cufflinks v%s to benefit from the most recent features and bug fixes (http://cufflinks.cbcb.umd.edu).\n", curr_version);
+		
+	}
+	else 
+	{
+		fprintf(stderr, "Warning: Could not connect to update server to verify current version. Please check at the Cufflinks website (http://cufflinks.cbcb.umd.edu).\n");
+	}
+}

-- 
Transcript assembly, differential expression, and differential regulation for RNA-Seq.



More information about the debian-med-commit mailing list