3070de3ae68503b4913f650584ee86aa606b5592
angie
  Mon Apr 28 16:03:21 2025 -0700
Fix corner case (exonic portion of alt in deletion spanning end of transcript) found in #35577

diff --git src/hg/lib/variantProjector.c src/hg/lib/variantProjector.c
index ac227e283c9..e74e5f07a9f 100644
--- src/hg/lib/variantProjector.c
+++ src/hg/lib/variantProjector.c
@@ -1708,30 +1708,41 @@
         vpTxEx->end.aliBlkIx = isRc ? vpTxIn->end.aliBlkIx + 1 : vpTxIn->end.aliBlkIx;
     else
         vpTxEx->end.aliBlkIx = vpTxIn->end.aliBlkIx;
     gSeqTrim = vpTxIn->end.gDistance;
     }
 vpTxEx->txName = cloneString(vpTxIn->txName);
 // Truncate alleles to just the exon part.
 int gRefLen = strlen(vpTxIn->gRef);
 if (gRefLen <= gSeqOffset)
     vpTxEx->gRef = cloneString("");
 else
     vpTxEx->gRef = cloneStringZ(vpTxIn->gRef + gSeqOffset, gRefLen - gSeqOffset - gSeqTrim);
 int gAltLen = strlen(vpTxIn->gAlt);
 if (gAltLen <= gSeqOffset)
     vpTxEx->gAlt = cloneString("");
+else if (gAltLen < gRefLen)
+    {
+    // If it's a deletion then we can't just apply the same -gSeqTrim adjustment to alt as to ref
+    // or else we could underflow.  Clip to the in-exon length that we used for vpTxEx->gRef.
+    // In real life, who knows what would happen to the exon boundary, but at least this is
+    // consistent with txAlt length.  VAI calls this complex_transcript_variant. #35577 note 14
+    int exonAltLen = gAltLen - gSeqOffset;
+    if (exonAltLen > gRefLen - gSeqOffset - gSeqTrim)
+        exonAltLen = gRefLen - gSeqOffset - gSeqTrim;
+    vpTxEx->gAlt = cloneStringZ(vpTxIn->gAlt + gSeqOffset, exonAltLen);
+    }
 else
     vpTxEx->gAlt = cloneStringZ(vpTxIn->gAlt + gSeqOffset, gAltLen - gSeqOffset - gSeqTrim);
 vpTxEx->txRef = cloneString(vpTxIn->txRef);
 vpTxEx->txAlt = cloneString(vpTxIn->txAlt);
 vpTxEx->basesShifted = vpTxIn->basesShifted;
 vpTxEx->genomeMismatch = vpTxIn->genomeMismatch;
 return vpTxEx;
 }
 
 static struct vpTx *vpTxNewIntronPart(struct vpTx *vpTxIn, struct psl *psl)
 /* vpTxIn either starts or ends in an intron; return a new vpTx that contains only the
  * intronic part. */
 {
 enum vpTxRegion startRegion = vpTxIn->start.region;
 enum vpTxRegion endRegion = vpTxIn->end.region;