/* [MS-XLSB] 2.4.107 BrtBeginMergeCells
324100
function write_BrtBeginMergeCells(cnt, o) {
	if(o == null) o = new_buf(4);
	o.write_shift(4, cnt);
	return o;
}
979731
function _getLtbr (context, mergedStyle) {
  return ['left', 'top', 'bottom', 'right'].reduce((pre, key) => {
    const msv = mergedStyle && mergedStyle[key]
    // undefined, null, or '0px' -> o
    pre[key] = msv && parseFloat(msv) || 0
    return pre
  }, {})
}
147867
function parse_BrtFmt(data, length/*:number*/) {
	var numFmtId = data.read_shift(2);
	var stFmtCode = parse_XLWideString(data,length-2);
	return [numFmtId, stFmtCode];
}
434980
function parse_BrtCellError(data, length) {
	var cell = parse_XLSBCell(data);
	var bError = data.read_shift(1);
	return [cell, bError, 'e'];
}
1202042
public static void mergeBloomFilterByteBuffers(
      ByteBuffer bf1Buffer,
      int bf1Start,
      ByteBuffer bf2Buffer,
      int bf2Start
  )
  {
    ByteBuffer view1 = bf1Buffer.duplicate().order(ByteOrder.BIG_ENDIAN);
    ByteBuffer view2 = bf2Buffer.duplicate().order(ByteOrder.BIG_ENDIAN);
    final int bf1Length = START_OF_SERIALIZED_LONGS + (view1.getInt(1 + bf1Start) * Long.BYTES);
    final int bf2Length = START_OF_SERIALIZED_LONGS + (view2.getInt(1 + bf2Start) * Long.BYTES);

    if (bf1Length != bf2Length) {
      throw new IllegalArgumentException("bf1Length " + bf1Length + " does not match bf2Length " + bf2Length);
    }

    // Validation on the bitset size/3 hash functions.
    for (int idx = 0; idx < START_OF_SERIALIZED_LONGS; ++idx) {
      if (view1.get(bf1Start + idx) != view2.get(bf2Start + idx)) {
        throw new IllegalArgumentException("bf1 NumHashFunctions/NumBits does not match bf2");
      }
    }

    // Just bitwise-OR the bits together - size/# functions should be the same,
    // rest of the data is serialized long values for the bitset which are supposed to be bitwise-ORed.
    for (int idx = START_OF_SERIALIZED_LONGS; idx < bf1Length; ++idx) {
      final int pos1 = bf1Start + idx;
      final int pos2 = bf2Start + idx;
      view1.put(pos1, (byte) (view1.get(pos1) | view2.get(pos2)));
    }
  }
749356
public static void mergeCellsHorizonal(XWPFTable table, int row, int fromCol, int toCol) {
        if (toCol <= fromCol) return;
        XWPFTableCell cell = table.getRow(row).getCell(fromCol);
        CTTcPr tcPr = getTcPr(cell);
        XWPFTableRow rowTable = table.getRow(row);
        for (int colIndex = fromCol + 1; colIndex <= toCol; colIndex++) {
            rowTable.getCtRow().removeTc(fromCol + 1);
            rowTable.removeCell(fromCol + 1);
        }

        tcPr.addNewGridSpan();
        tcPr.getGridSpan().setVal(BigInteger.valueOf((long) (toCol - fromCol + 1)));
    }
483344
function _updateMergeStartIndex(tableData, startColIndex, endColIndex) {
  tableData.forEach(rowData => {
    rowData.slice(endColIndex + 1).forEach(cellData => {
      if (util.isExisty(cellData.colMergeWith) && cellData.colMergeWith >= startColIndex) {
        cellData.colMergeWith = endColIndex + 1;
      }
    });
  });
}
719274
function(textNodes, range) {
            log.group("postApply");
            var firstNode = textNodes[0], lastNode = textNodes[textNodes.length - 1];

            var merges = [], currentMerge;

            var rangeStartNode = firstNode, rangeEndNode = lastNode;
            var rangeStartOffset = 0, rangeEndOffset = lastNode.length;

            var textNode, precedingTextNode;

            for (var i = 0, len = textNodes.length; i < len; ++i) {
                textNode = textNodes[i];
                precedingTextNode = getAdjacentMergeableTextNode(textNode, false);
                log.debug("Checking for merge. text node: " + textNode.data + ", preceding: " + (precedingTextNode ? precedingTextNode.data : null));
                if (precedingTextNode) {
                    if (!currentMerge) {
                        currentMerge = new Merge(precedingTextNode);
                        merges.push(currentMerge);
                    }
                    currentMerge.textNodes.push(textNode);
                    if (textNode === firstNode) {
                        rangeStartNode = currentMerge.firstTextNode;
                        rangeStartOffset = rangeStartNode.length;
                    }
                    if (textNode === lastNode) {
                        rangeEndNode = currentMerge.firstTextNode;
                        rangeEndOffset = currentMerge.getLength();
                    }
                } else {
                    currentMerge = null;
                }
            }

            // Test whether the first node after the range needs merging
            var nextTextNode = getAdjacentMergeableTextNode(lastNode, true);

            if (nextTextNode) {
                if (!currentMerge) {
                    currentMerge = new Merge(lastNode);
                    merges.push(currentMerge);
                }
                currentMerge.textNodes.push(nextTextNode);
            }

            // Do the merges
            if (merges.length) {
                log.info("Merging. Merges:", merges);
                for (i = 0, len = merges.length; i < len; ++i) {
                    merges[i].doMerge();
                }
                log.info(rangeStartNode.nodeValue, rangeStartOffset, rangeEndNode.nodeValue, rangeEndOffset);

                // Set the range boundaries
                range.setStart(rangeStartNode, rangeStartOffset);
                range.setEnd(rangeEndNode, rangeEndOffset);
            }
            log.groupEnd();
        }
1207942
function parse_BrtRowHdr(data, length) {
	var z = ({});
	var tgt = data.l + length;
	z.r = data.read_shift(4);
	data.l += 4; // TODO: ixfe
	var miyRw = data.read_shift(2);
	data.l += 1; // TODO: top/bot padding
	var flags = data.read_shift(1);
	data.l = tgt;
	if(flags & 0x07) z.level = flags & 0x07;
	if(flags & 0x10) z.hidden = true;
	if(flags & 0x20) z.hpt = miyRw / 20;
	return z;
}
1075701
function parse_BrtShrFmla(data, length, opts) {
	var end = data.l + length;
	var rfx = parse_UncheckedRfX(data, 16);
	var o = [rfx];
	if(opts.cellFormula) {
		var formula = parse_XLSBSharedParsedFormula(data, end - data.l, opts);
		o[1] = formula;
		data.l = end;
	} else data.l = end;
	return o;
}
973749
private void directMergeRows(MergeRow mergeRowAnn, int fromRow, int lastRow, int col) {
        fixCellValue(mergeRowAnn, fromRow, col);

        if (lastRow < fromRow) return;
        if (lastRow == fromRow && mergeRowAnn.moreCols() == 0) return;

        val c1 = new CellRangeAddress(fromRow, lastRow, col, col + mergeRowAnn.moreCols());
        sheet.addMergedRegion(c1);
    }
1305127
function parse_BrtBeginComment(data, length) {
	var out = {};
	out.iauthor = data.read_shift(4);
	var rfx = parse_UncheckedRfX(data, 16);
	out.rfx = rfx.s;
	out.ref = encode_cell(rfx.s);
	data.l += 16; /*var guid = parse_GUID(data); */
	return out;
}
905421
@SuppressWarnings("unchecked")
  private int mergeRange(ParentSoyNode<?> parent, int start, int lastNonEmptyRawTextNode, int end) {
    checkArgument(start < end);
    if (start == -1 || end == start + 1) {
      return end;
    }
    // general case, there are N rawtextnodes to merge where n > 1
    // merge all the nodes together, then drop all the raw text nodes from the end
    RawTextNode newNode =
        RawTextNode.concat(
            (List<RawTextNode>) parent.getChildren().subList(start, lastNonEmptyRawTextNode + 1));
    ((ParentSoyNode) parent).replaceChild(start, newNode);
    for (int i = end - 1; i > start; i--) {
      parent.removeChild(i);
    }
    return start + 1;
  }
274370
private void bfs(int[] membership, int cluster, int id) {
        int n = merge.length + 1;
        Queue<Integer> queue = new LinkedList<>();
        queue.offer(cluster);

        for (Integer i = queue.poll(); i != null; i = queue.poll()) {
            if (i < n) {
                membership[i] = id;
                continue;
            }

            i -= n;

            int m1 = merge[i][0];

            if (m1 >= n) {
                queue.offer(m1);
            } else {
                membership[m1] = id;
            }

            int m2 = merge[i][1];

            if (m2 >= n) {
                queue.offer(m2);
            } else {
                membership[m2] = id;
            }
        }
    }
815878
function parse_BrtFmlaBool(data, length, opts) {
	var end = data.l + length;
	var cell = parse_XLSBCell(data);
	cell.r = opts['!row'];
	var value = data.read_shift(1);
	var o = [cell, value, 'b'];
	if(opts.cellFormula) {
		data.l += 2;
		var formula = parse_XLSBCellParsedFormula(data, end - data.l, opts);
		o[3] = stringify_formula(formula, null/*range*/, cell, opts.supbooks, opts);/* TODO */
	}
	else data.l = end;
	return o;
}
1342379
function parse_BrtColor(data/*::, length*/) {
	var out = {};
	var d = data.read_shift(1);

	//var fValidRGB = d & 1;
	var xColorType = d >>> 1;

	var index = data.read_shift(1);
	var nTS = data.read_shift(2, 'i');
	var bR = data.read_shift(1);
	var bG = data.read_shift(1);
	var bB = data.read_shift(1);
	data.l++; //var bAlpha = data.read_shift(1);

	switch(xColorType) {
		case 0: out.auto = 1; break;
		case 1:
			out.index = index;
			var icv = XLSIcv[index];
			/* automatic pseudo index 81 */
			if(icv) out.rgb = rgb2Hex(icv);
			break;
		case 2:
			/* if(!fValidRGB) throw new Error("invalid"); */
			out.rgb = rgb2Hex([bR, bG, bB]);
			break;
		case 3: out.theme = index; break;
	}
	if(nTS != 0) out.tint = nTS > 0 ? nTS / 32767 : nTS / 32768;

	return out;
}
708516
function parse_XLSBCell(data)/*:any*/ {
	var col = data.read_shift(4);
	var iStyleRef = data.read_shift(2);
	iStyleRef += data.read_shift(1) <<16;
	data.l++; //var fPhShow = data.read_shift(1);
	return { c:col, iStyleRef: iStyleRef };
}