/**
   * 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;
  }
 /** Returns whether a mapping is the identity. */
 public static boolean isIdentity(TargetMapping mapping) {
   if (mapping.getSourceCount() != mapping.getTargetCount()) {
     return false;
   }
   for (int i = 0; i < mapping.getSourceCount(); i++) {
     if (mapping.getTargetOpt(i) != i) {
       return false;
     }
   }
   return true;
 }
 /**
  * Returns a mapping that shifts a given mapping's target by a given offset.
  *
  * <p>For example, given {@code mapping} with sourceCount=2, targetCount=8, and (source, target)
  * entries {[0: 5], [1: 7]}, offsetTarget(mapping, 3) returns a mapping with sourceCount=2,
  * targetCount=11, and (source, target) entries {[0: 8], [1: 10]}.
  *
  * @param mapping Input mapping
  * @param offset Offset to be applied to each target
  * @param targetCount New target count; must be at least {@code mapping}'s target count plus
  *     {@code offset}
  * @return Shifted mapping
  */
 public static TargetMapping offsetTarget(
     final TargetMapping mapping, final int offset, final int targetCount) {
   if (targetCount < mapping.getTargetCount() + offset) {
     throw new IllegalArgumentException("new target count too low");
   }
   return target(
       new Function1<Integer, Integer>() {
         public Integer apply(Integer source) {
           int target = mapping.getTargetOpt(source);
           return target < 0 ? null : target + offset;
         }
       },
       mapping.getSourceCount(),
       targetCount);
 }
 public int getTarget(int source) {
   if (source == this.source) {
     return this.target;
   } else {
     return parent.getTarget(source);
   }
 }
 /**
  * Returns a mapping that shifts a given mapping's source by a given offset.
  *
  * <p>For example, given {@code mapping} with sourceCount=2, targetCount=8, and (source, target)
  * entries {[0: 5], [1: 7]}, offsetSource(mapping, 3) returns a mapping with sourceCount=5,
  * targetCount=8, and (source, target) entries {[3: 5], [4: 7]}.
  *
  * @param mapping Input mapping
  * @param offset Offset to be applied to each source
  * @param sourceCount New source count; must be at least {@code mapping}'s source count plus
  *     {@code offset}
  * @return Shifted mapping
  */
 public static TargetMapping offsetSource(
     final TargetMapping mapping, final int offset, final int sourceCount) {
   if (sourceCount < mapping.getSourceCount() + offset) {
     throw new IllegalArgumentException("new source count too low");
   }
   return target(
       new Function1<Integer, Integer>() {
         public Integer apply(Integer source) {
           int source2 = source - offset;
           return source2 < 0 || source2 >= mapping.getSourceCount()
               ? null
               : mapping.getTargetOpt(source2);
         }
       },
       sourceCount,
       mapping.getTargetCount());
 }
 /**
  * Creates a mapping by appending two mappings.
  *
  * <p>Sources and targets of the second mapping are shifted to the right.
  *
  * <p>For example,
  *
  * <pre>append({0:0, 1:1}, {0:0, 1:1, 2:2})</pre>
  *
  * yields
  *
  * <pre>{0:0, 1:1, 2:2, 3:3, 4:4}</pre>
  *
  * .
  *
  * @see #merge
  */
 public static TargetMapping append(TargetMapping mapping0, TargetMapping mapping1) {
   final int s0 = mapping0.getSourceCount();
   final int s1 = mapping1.getSourceCount();
   final int t0 = mapping0.getTargetCount();
   final int t1 = mapping1.getTargetCount();
   final TargetMapping mapping = create(MappingType.INVERSE_SURJECTION, s0 + s1, t0 + t1);
   for (int s = 0; s < s0; s++) {
     int t = mapping0.getTargetOpt(s);
     if (t >= 0) {
       mapping.set(s, t);
     }
   }
   for (int s = 0; s < s1; s++) {
     int t = mapping1.getTargetOpt(s);
     if (t >= 0) {
       mapping.set(s0 + s, t0 + t);
     }
   }
   return mapping;
 }
 /**
  * Returns a mapping that shifts a given mapping's target by a given offset, incrementing the
  * number of targets by the minimum possible.
  *
  * @param mapping Input mapping
  * @param offset Offset to be applied to each target
  * @return Shifted mapping
  */
 public static TargetMapping offsetTarget(final TargetMapping mapping, final int offset) {
   return offsetTarget(mapping, offset, mapping.getTargetCount() + offset);
 }
 /**
  * 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;
 }
 public MappingType getMappingType() {
   // FIXME: Mapping type might be weaker than parent.
   return parent.getMappingType();
 }
 public Mapping inverse() {
   return new OverridingSourceMapping(parent.inverse(), source, target);
 }
 public void set(int source, int target) {
   parent.set(source, target);
 }
 public int size() {
   return parent.getTargetOpt(source) >= 0 ? parent.size() : parent.size() + 1;
 }