/**
  * Creates a mapping by merging two mappings. There must be no clashes.
  *
  * <p>Unlike {@link #append}, sources and targets are not shifted.
  *
  * <p>For example, <code>merge({0:0, 1:1}, {2:2, 3:3, 4:4})</code> yields <code>
  * {0:0, 1:1, 2:2, 3:3, 4:4}</code>. <code>merge({0:0, 1:1}, {1:2, 2:3})</code> throws, because
  * there are two entries with source=1.
  */
 public static TargetMapping merge(TargetMapping mapping0, TargetMapping mapping1) {
   final int s0 = mapping0.getSourceCount();
   final int s1 = mapping1.getSourceCount();
   final int sMin = Math.min(s0, s1);
   final int sMax = Math.max(s0, s1);
   final int t0 = mapping0.getTargetCount();
   final int t1 = mapping1.getTargetCount();
   final int tMax = Math.max(t0, t1);
   final TargetMapping mapping = create(MappingType.INVERSE_SURJECTION, sMax, tMax);
   for (int s = 0; s < sMin; s++) {
     int t = mapping0.getTargetOpt(s);
     if (t >= 0) {
       mapping.set(s, t);
       assert mapping1.getTargetOpt(s) < 0;
     } else {
       t = mapping1.getTargetOpt(s);
       if (t >= 0) {
         mapping.set(s, t);
       }
     }
   }
   for (int s = sMin; s < sMax; s++) {
     int t = s < s0 ? mapping0.getTargetOpt(s) : -1;
     if (t >= 0) {
       mapping.set(s, t);
       assert mapping1.getTargetOpt(s) < 0;
     } else {
       t = s < s1 ? mapping1.getTargetOpt(s) : -1;
       if (t >= 0) {
         mapping.set(s, t);
       }
     }
   }
   return mapping;
 }
  /**
   * Creates a mapping that consists of a set of contiguous ranges.
   *
   * <p>For example,
   *
   * <pre>createShiftMapping(60,
   *     100, 0, 3,
   *     200, 50, 5);
   * </pre>
   *
   * <p>creates
   *
   * <table>
   * <caption>Example mapping</caption>
   * <tr><th>Source</th><th>Target</th></tr>
   * <tr><td>0</td><td>100</td></tr>
   * <tr><td>1</td><td>101</td></tr>
   * <tr><td>2</td><td>102</td></tr>
   * <tr><td>3</td><td>-1</td></tr>
   * <tr><td>...</td><td>-1</td></tr>
   * <tr><td>50</td><td>200</td></tr>
   * <tr><td>51</td><td>201</td></tr>
   * <tr><td>52</td><td>202</td></tr>
   * <tr><td>53</td><td>203</td></tr>
   * <tr><td>54</td><td>204</td></tr>
   * <tr><td>55</td><td>-1</td></tr>
   * <tr><td>...</td><td>-1</td></tr>
   * <tr><td>59</td><td>-1</td></tr>
   * </table>
   *
   * @param sourceCount Maximum value of {@code source}
   * @param ints Collection of ranges, each {@code (target, source, count)}
   * @return Mapping that maps from source ranges to target ranges
   */
  public static TargetMapping createShiftMapping(int sourceCount, int... ints) {
    int targetCount = 0;
    assert ints.length % 3 == 0;
    for (int i = 0; i < ints.length; i += 3) {
      final int target = ints[i];
      final int length = ints[i + 2];
      final int top = target + length;
      targetCount = Math.max(targetCount, top);
    }
    final TargetMapping mapping =
        create(
            MappingType.INVERSE_SURJECTION,
            sourceCount, // aCount + bCount + cCount,
            targetCount); // cCount + bCount

    for (int i = 0; i < ints.length; i += 3) {
      final int target = ints[i];
      final int source = ints[i + 1];
      final int length = ints[i + 2];
      assert source + length <= sourceCount;
      for (int j = 0; j < length; j++) {
        assert mapping.getTargetOpt(source + j) == -1;
        mapping.set(source + j, target + j);
      }
    }
    return mapping;
  }