@Override
    public LongRangeSet mul(LongRangeSet multiplier, LongRangeType lrType) {
      if (multiplier.isEmpty()) return multiplier;
      if (myValue == 0) return this;
      if (myValue == 1) return multiplier;
      if (myValue == -1) return multiplier.negate(lrType);
      if (multiplier instanceof Point) {
        long val = ((Point)multiplier).myValue;
        long res = myValue * val;
        return point(lrType.cast(res));
      }
      boolean overflow = false;
      long min = multiplier.min();
      long max = multiplier.max();
      if (lrType == LongRangeType.INT64) {
        try {
          min = Math.multiplyExact(min, myValue);
          max = Math.multiplyExact(max, myValue);
        }
        catch (ArithmeticException e) {
          overflow = true;
        }
      }
      else {
        assert lrType.bytes() <= 4;
        min *= myValue;
        max *= myValue;
        if (min != lrType.cast(min) || max != lrType.cast(max)) {
          overflow = true;
        }
      }
      LongRangeSet result;
      if (overflow) {
        result = lrType.fullRange();
      }
      else {
        result = min > max ? range(max, min) : range(min, max);
      }
      long abs = Math.abs(myValue);
      if (overflow) {
        abs = Long.lowestOneBit(abs);
      }
      if (abs < 0 || (abs > Long.SIZE && Long.bitCount(abs) == 1)) {
        abs = Long.SIZE;
      }
      if (multiplier instanceof ModRange && ((ModRange)multiplier).myBits == 1 && abs < Long.SIZE) {
        int mod = ((ModRange)multiplier).myMod;
        abs *= overflow ? Long.lowestOneBit(mod) : mod;
        if (abs < 0 || (abs > Long.SIZE && Long.bitCount(abs) == 1)) {
          abs = Long.SIZE;
        }
      }
      return modRange(result.min(), result.max(), abs, 1L);
    }
@Override
    public LongRangeSet mul(LongRangeSet multiplier, LongRangeType lrType)