47a8767ba69ad593d45adb123c189cee3a0f5f85
markd
  Thu Aug 27 20:23:01 2020 -0700
added -partSize option to bedPartition to allow grouping multiple partitions into a range

diff --git src/hg/utils/bedPartition/bedPartition.c src/hg/utils/bedPartition/bedPartition.c
index 7c43ece..b9f4e9e 100644
--- src/hg/utils/bedPartition/bedPartition.c
+++ src/hg/utils/bedPartition/bedPartition.c
@@ -2,47 +2,52 @@
 
 /* Copyright (C) 2019 The Regents of the University of California 
  * See README in this or parent directory for licensing information. */
 #include "common.h"
 #include "options.h"
 #include "partitionSort.h"
 #include "basicBed.h"
 #include "sqlNum.h"
 #include "dystring.h"
 #include "portable.h"
 
 
 /* command line options and values */
 static struct optionSpec optionSpecs[] =
 {
+    {"partSize", OPTION_INT},
     {"parallel", OPTION_INT},
     {NULL, 0}
 };
+static int gPartSize = 1;
 static int gParallel = 0;
 
 static void usage()
 /* Explain usage and exit. */
 {
 errAbort("bedPartition - split BED ranges into non-overlapping ranges\n"
   "usage:\n"
   "   bedPartition [options] bedFile rangesBed\n"
   "\n"
   "Split ranges in a BED into non-overlapping sets for use in cluster jobs.\n"
   "Output is a BED 3 of the ranges.\n"
   "The bedFile maybe compressed and no ordering is assumed.\n"
   "\n"
   "options:\n"
+  "   -partSize=1 - will combine non-overlapping partitions, up to\n"
+  "    this number of ranges.\n"
+  "    per set of overlapping records.\n"
   "   -parallel=n - use this many cores for parallel sorting\n");
 }
 
 struct bedInput
 /* object to read a bed */
 {
     struct pipeline *pl;     /* sorting pipeline */
     struct lineFile *lf;     /* lineFile to pipeline */
     struct bed3 *pending;     /* next bed to read, if not NULL */
 };
 
 static struct bedInput *bedInputNew(char *bedFile)
 /* create object to read BEDs */
 {
 struct bedInput *bi;
@@ -82,75 +87,88 @@
 if (bed != NULL)
     bi->pending = NULL;
 else
     bed = bed3Next(bi->lf);
 return bed;
 }
 
 static void bedInputPutBack(struct bedInput *bi, struct bed3 *bed)
 /* save bed pending slot. */
 {
 if (bi->pending)
     errAbort("only one bed maybe pending");
 bi->pending = bed;
 }
 
+static boolean sameChrom(struct bed3 *bed, struct bed3 *bedPart)
+/* chrom the same? */
+{
+return sameString(bed->chrom, bedPart->chrom);
+}
+
 static boolean isOverlapped(struct bed3 *bed, struct bed3 *bedPart)
 /* determine if a bed is in the partition */
 {
 return (bed->chromStart < bedPart->chromEnd) && (bed->chromEnd > bedPart->chromStart)
-    && sameString(bed->chrom, bedPart->chrom);
+    && sameChrom(bed, bedPart);
 }
 
+
 static struct bed3 *readPartition(struct bedInput *bi)
 /* read next set of overlapping beds */
 {
 struct bed3 *bedPart = bedInputNext(bi);
 struct bed3 *bed;
 
 if (bedPart == NULL)
     return NULL;  /* no more */
-/* add more beds while they overlap, easy since input is sorted */
+/* add more beds while they overlap, due to way input is sorted
+ * with by start and then reverse end */
+
+int partCnt = 1; // already have one
 while ((bed = bedInputNext(bi)) != NULL)
     {
-    if (isOverlapped(bed, bedPart))
+    boolean samePart = isOverlapped(bed, bedPart);
+    if (samePart || ((partCnt < gPartSize) && sameChrom(bed, bedPart)))
         {
         bedPart->chromStart = min(bedPart->chromStart, bed->chromStart);
         bedPart->chromEnd = max(bedPart->chromEnd, bed->chromEnd);
         bed3Free(&bed);
+        if (!samePart)
+            partCnt++;
         }
     else
         {
         bedInputPutBack(bi, bed);
         break;
         }
     }
-
 return bedPart;
 }
 
 static void bedPartition(char *bedFile, char *rangesBed)
 /* split BED files into non-overlapping sets */
 {
 struct bedInput *bi = bedInputNew(bedFile);
 struct bed3 *bedPart;
 FILE *outFh = mustOpen(rangesBed, "w");
 
 while ((bedPart = readPartition(bi)) != NULL)
     {
     fprintf(outFh, "%s\t%d\t%d\n", bedPart->chrom, bedPart->chromStart, bedPart->chromEnd);
     bed3Free(&bedPart);
     }
 carefulClose(&outFh);
 bedInputFree(&bi);
 }
 
 int main(int argc, char *argv[])
 /* Process command line. */
 {
 optionInit(&argc, argv, optionSpecs);
 if (argc != 3)
     usage();
+gPartSize = optionInt("partSize", gPartSize);
 gParallel = optionInt("parallel", gParallel);
 bedPartition(argv[1], argv[2]);
 return 0;
 }