예제 #1
0
/**
 * Copies all attributes from source to target including 'names', 'dimNames' and 'dim' (unlike
 * {@link CopyOfRegAttributesNode}), additionally removes the 'dim' from the result if it is not
 * present in the source.
 *
 * <p>TODO: this logic is duplicated in RVector#copyRegAttributesFrom and UnaryMapNode, but behind
 * TruffleBoundary, does it have a reason for TruffleBoundary? Can we replace it with this node?
 */
public abstract class UnaryCopyAttributesNode extends RBaseNode {

  protected final boolean copyAllAttributes;

  @Child protected HasFixedAttributeNode hasDimNode = HasFixedAttributeNode.createDim();
  @Child protected GetDimNamesAttributeNode getDimNamesNode = GetDimNamesAttributeNode.create();
  @Child protected GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();

  protected UnaryCopyAttributesNode(boolean copyAllAttributes) {
    this.copyAllAttributes = copyAllAttributes;
  }

  public static UnaryCopyAttributesNode create() {
    return UnaryCopyAttributesNodeGen.create(true);
  }

  public abstract RAbstractVector execute(RAbstractVector target, RAbstractVector left);

  protected boolean containsMetadata(RAbstractVector vector) {
    return vector instanceof RVector && hasDimNode.execute(vector)
        || (copyAllAttributes && vector.getAttributes() != null)
        || getNamesNode.getNames(vector) != null
        || getDimNamesNode.getDimNames(vector) != null;
  }

  @SuppressWarnings("unused")
  @Specialization(guards = "!containsMetadata(source)")
  protected RAbstractVector copyNoMetadata(RAbstractVector target, RAbstractVector source) {
    return target;
  }

  @SuppressWarnings("unused")
  @Specialization(guards = {"copyAllAttributes", "target == source"})
  protected RAbstractVector copySameVector(RAbstractVector target, RAbstractVector source) {
    return target;
  }

  @Specialization(guards = {"!copyAllAttributes || target != source", "containsMetadata(source)"})
  protected RAbstractVector copySameLength(
      RAbstractVector target,
      RAbstractVector source, //
      @Cached("create()") CopyOfRegAttributesNode copyOfReg, //
      @Cached("createDim()") RemoveFixedAttributeNode removeDim, //
      @Cached("createDimNames()") RemoveFixedAttributeNode removeDimNames, //
      @Cached("create()") InitAttributesNode initAttributes, //
      @Cached("createNames()") SetFixedAttributeNode putNames, //
      @Cached("createDim()") SetFixedAttributeNode putDim, //
      @Cached("createDimNames()") SetFixedAttributeNode putDimNames, //
      @Cached("createBinaryProfile()") ConditionProfile noDimensions, //
      @Cached("createBinaryProfile()") ConditionProfile hasNamesSource, //
      @Cached("createBinaryProfile()") ConditionProfile hasDimNames,
      @Cached("create()") GetDimAttributeNode getDimsNode) {
    RVector<?> result = target.materialize();

    if (copyAllAttributes) {
      copyOfReg.execute(source, result);
    }

    int[] newDimensions = getDimsNode.getDimensions(source);
    if (noDimensions.profile(newDimensions == null)) {
      DynamicObject attributes = result.getAttributes();
      if (attributes != null) {
        removeDim.execute(attributes);
        removeDimNames.execute(attributes);
      }

      RStringVector vecNames = getNamesNode.getNames(source);
      if (hasNamesSource.profile(vecNames != null)) {
        putNames.execute(initAttributes.execute(result), vecNames);
        return result;
      }
      return result;
    }

    putDim.execute(
        initAttributes.execute(result),
        RDataFactory.createIntVector(newDimensions, RDataFactory.COMPLETE_VECTOR));

    RList newDimNames = getDimNamesNode.getDimNames(source);
    if (hasDimNames.profile(newDimNames != null)) {
      putDimNames.execute(result.getAttributes(), newDimNames);
      newDimNames.elementNamePrefix = RRuntime.DIMNAMES_LIST_ELEMENT_NAME_PREFIX;
      return result;
    }
    return result;
  }
}
예제 #2
0
파일: Repeat.java 프로젝트: graalvm/fastr
/**
 * The {@code rep} builtin works as follows.
 *
 * <ol>
 *   <li>If {@code each} is greater than one, all elements of {@code x} are first replicated {@code
 *       each} times.
 *   <li>If {@code length.out} is given, the result of the first step is truncated or extended as
 *       required. In this case, {@code times} is ignored.
 *   <li>If {@code length.out} is not given, {@code times} is regarded:
 *       <ul>
 *         <li>If {@code times} is a one-element vector, the result of the first step is replicated
 *             {@code times} times.
 *         <li>If {@code times} is a vector longer than one, and {@code each} is greater than one,
 *             an error is issued.
 *         <li>If {@code times} is a vector longer than one, and {@code each} is one, and {@code
 *             times} is as long as {@code x}, each element of {@code x} is given the number of
 *             times indicated by the value at the same index of {@code times}. If {@code times} has
 *             a different length, an error is issued.
 *       </ul>
 * </ol>
 */
@RBuiltin(
    name = "rep",
    kind = PRIMITIVE,
    parameterNames = {"x", "times", "length.out", "each"},
    dispatch = INTERNAL_GENERIC,
    behavior = PURE)
public abstract class Repeat extends RBuiltinNode {

  protected abstract Object execute(
      RAbstractVector x, RAbstractIntVector times, int lengthOut, int each);

  private final ConditionProfile lengthOutOrTimes = ConditionProfile.createBinaryProfile();
  private final BranchProfile errorBranch = BranchProfile.create();
  private final ConditionProfile oneTimeGiven = ConditionProfile.createBinaryProfile();
  private final ConditionProfile replicateOnce = ConditionProfile.createBinaryProfile();
  @Child private GetNamesAttributeNode getNames = GetNamesAttributeNode.create();

  @Override
  public Object[] getDefaultParameterValues() {
    return new Object[] {RMissing.instance, 1, RRuntime.INT_NA, 1};
  }

  private String argType(Object arg) {
    return ((RTypedValue) arg).getRType().getName();
  }

  @Override
  protected void createCasts(CastBuilder casts) {
    Function<Object, Object> argType = this::argType;
    casts.arg("x").mustBe(abstractVectorValue(), RError.Message.ATTEMPT_TO_REPLICATE, argType);
    casts
        .arg("times")
        .defaultError(RError.Message.INVALID_ARGUMENT, "times")
        .mustNotBeNull()
        .asIntegerVector();
    casts
        .arg("length.out")
        .mustNotBeNull()
        .asIntegerVector()
        .shouldBe(size(1).or(size(0)), RError.Message.FIRST_ELEMENT_USED, "length.out")
        .findFirst(RRuntime.INT_NA, RError.Message.FIRST_ELEMENT_USED, "length.out")
        .mustBe(intNA().or(gte(0)));
    casts
        .arg("each")
        .asIntegerVector()
        .shouldBe(size(1).or(size(0)), RError.Message.FIRST_ELEMENT_USED, "each")
        .findFirst(1, RError.Message.FIRST_ELEMENT_USED, "each")
        .notNA(1)
        .mustBe(gte(0));
  }

  protected boolean hasNames(RAbstractVector x) {
    return getNames.getNames(x) != null;
  }

  private RError invalidTimes() {
    throw RError.error(this, RError.Message.INVALID_ARGUMENT, "times");
  }

  @Specialization(
      guards = {"x.getLength() == 1", "times.getLength() == 1", "each <= 1", "!hasNames(x)"})
  protected RAbstractVector repNoEachNoNamesSimple(
      RAbstractDoubleVector x,
      RAbstractIntVector times,
      int lengthOut,
      @SuppressWarnings("unused") int each) {
    int t = times.getDataAt(0);
    if (t < 0) {
      errorBranch.enter();
      throw invalidTimes();
    }
    int length = lengthOutOrTimes.profile(!RRuntime.isNA(lengthOut)) ? lengthOut : t;
    double[] data = new double[length];
    Arrays.fill(data, x.getDataAt(0));
    return RDataFactory.createDoubleVector(data, !RRuntime.isNA(x.getDataAt(0)));
  }

  @Specialization(guards = {"each > 1", "!hasNames(x)"})
  protected RAbstractVector repEachNoNames(
      RAbstractVector x, RAbstractIntVector times, int lengthOut, int each) {
    if (times.getLength() > 1) {
      errorBranch.enter();
      throw invalidTimes();
    }
    RAbstractVector input = handleEach(x, each);
    if (lengthOutOrTimes.profile(!RRuntime.isNA(lengthOut))) {
      return handleLengthOut(input, lengthOut, false);
    } else {
      return handleTimes(input, times, false);
    }
  }

  @Specialization(guards = {"each <= 1", "!hasNames(x)"})
  protected RAbstractVector repNoEachNoNames(
      RAbstractVector x,
      RAbstractIntVector times,
      int lengthOut,
      @SuppressWarnings("unused") int each) {
    if (lengthOutOrTimes.profile(!RRuntime.isNA(lengthOut))) {
      return handleLengthOut(x, lengthOut, true);
    } else {
      return handleTimes(x, times, true);
    }
  }

  @Specialization(guards = {"each > 1", "hasNames(x)"})
  protected RAbstractVector repEachNames(
      RAbstractVector x,
      RAbstractIntVector times,
      int lengthOut,
      int each,
      @Cached("create()") InitAttributesNode initAttributes,
      @Cached("createNames()") SetFixedAttributeNode putNames) {
    if (times.getLength() > 1) {
      errorBranch.enter();
      throw invalidTimes();
    }
    RAbstractVector input = handleEach(x, each);
    RStringVector names = (RStringVector) handleEach(getNames.getNames(x), each);
    RVector<?> r;
    if (lengthOutOrTimes.profile(!RRuntime.isNA(lengthOut))) {
      names = (RStringVector) handleLengthOut(names, lengthOut, false);
      r = handleLengthOut(input, lengthOut, false);
    } else {
      names = (RStringVector) handleTimes(names, times, false);
      r = handleTimes(input, times, false);
    }
    putNames.execute(initAttributes.execute(r), names);
    return r;
  }

  @Specialization(guards = {"each <= 1", "hasNames(x)"})
  protected RAbstractVector repNoEachNames(
      RAbstractVector x,
      RAbstractIntVector times,
      int lengthOut,
      @SuppressWarnings("unused") int each,
      @Cached("create()") InitAttributesNode initAttributes,
      @Cached("createNames()") SetFixedAttributeNode putNames) {
    RStringVector names;
    RVector<?> r;
    if (lengthOutOrTimes.profile(!RRuntime.isNA(lengthOut))) {
      names = (RStringVector) handleLengthOut(getNames.getNames(x), lengthOut, true);
      r = handleLengthOut(x, lengthOut, true);
    } else {
      names = (RStringVector) handleTimes(getNames.getNames(x), times, true);
      r = handleTimes(x, times, true);
    }
    putNames.execute(initAttributes.execute(r), names);
    return r;
  }

  /** Prepare the input vector by replicating its elements. */
  private static RVector<?> handleEach(RAbstractVector x, int each) {
    RVector<?> r = x.createEmptySameType(x.getLength() * each, x.isComplete());
    for (int i = 0; i < x.getLength(); i++) {
      for (int j = i * each; j < (i + 1) * each; j++) {
        r.transferElementSameType(j, x, i);
      }
    }
    return r;
  }

  /** Extend or truncate the vector to a specified length. */
  private static RVector<?> handleLengthOut(
      RAbstractVector x, int lengthOut, boolean copyIfSameSize) {
    if (x.getLength() == lengthOut) {
      return (RVector<?>) (copyIfSameSize ? x.copy() : x);
    }
    return x.copyResized(lengthOut, false);
  }

  /** Replicate the vector a given number of times. */
  private RVector<?> handleTimes(
      RAbstractVector x, RAbstractIntVector times, boolean copyIfSameSize) {
    if (oneTimeGiven.profile(times.getLength() == 1)) {
      // only one times value is given
      final int howManyTimes = times.getDataAt(0);
      if (howManyTimes < 0) {
        errorBranch.enter();
        throw invalidTimes();
      }
      if (replicateOnce.profile(howManyTimes == 1)) {
        return (RVector<?>) (copyIfSameSize ? x.copy() : x);
      } else {
        return x.copyResized(x.getLength() * howManyTimes, false);
      }
    } else {
      // times is a vector with several elements
      if (x.getLength() != times.getLength()) {
        errorBranch.enter();
        invalidTimes();
      }
      // iterate once over the times vector to determine result vector size
      int resultLength = 0;
      for (int i = 0; i < times.getLength(); i++) {
        int t = times.getDataAt(i);
        if (t < 0) {
          errorBranch.enter();
          throw invalidTimes();
        }
        resultLength += t;
      }
      // create and populate result vector
      RVector<?> r = x.createEmptySameType(resultLength, x.isComplete());
      int wp = 0; // write pointer
      for (int i = 0; i < x.getLength(); i++) {
        for (int j = 0; j < times.getDataAt(i); ++j, ++wp) {
          r.transferElementSameType(wp, x, i);
        }
      }
      return r;
    }
  }
}