text:
package org.apache.spark.sql.execution.datasources.parquet;

import java.io.IOException;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Arrays;

import org.apache.parquet.bytes.ByteBufferInputStream;
import org.apache.parquet.bytes.BytesInput;
import org.apache.parquet.bytes.BytesUtils;
import org.apache.parquet.column.ColumnDescriptor;
import org.apache.parquet.column.Dictionary;
import org.apache.parquet.column.Encoding;
import org.apache.parquet.column.page.*;
import org.apache.parquet.column.values.ValuesReader;
import org.apache.parquet.io.api.Binary;
import org.apache.parquet.schema.OriginalType;
import org.apache.parquet.schema.PrimitiveType;

import org.apache.spark.sql.catalyst.util.DateTimeUtils;
import org.apache.spark.sql.catalyst.util.RebaseDateTime;
import org.apache.spark.sql.execution.datasources.DataSourceUtils;
import org.apache.spark.sql.execution.datasources.SchemaColumnConvertNotSupportedException;
import org.apache.spark.sql.execution.vectorized.WritableColumnVector;
import org.apache.spark.sql.types.DataTypes;
import org.apache.spark.sql.types.DecimalType;

import static org.apache.parquet.column.ValuesType.REPETITION_LEVEL;
import static org.apache.spark.sql.execution.datasources.parquet.SpecificParquetRecordReaderBase.ValuesReaderIntIterator;
import static org.apache.spark.sql.execution.datasources.parquet.SpecificParquetRecordReaderBase.createRLEIterator;

/**
 * Decoder to return values from a single column.
 */
public class VectorizedColumnReader {
  /**
   * Total number of values read.
   */
  private long valuesRead;

  /**
   * value that indicates the end of the current page. That is,
   * if valuesRead == endOfPageValueCount, we are at the end of the page.
   */
  private long endOfPageValueCount;

  /**
   * The dictionary, if this column has dictionary encoding.
   */
  private final Dictionary dictionary;

  /**
   * If true, the current page is dictionary encoded.
   */
  private boolean isCurrentPageDictionaryEncoded;

  /**
   * Maximum definition level for this column.
   */
  private final int maxDefLevel;

  /**
   * Repetition/Definition/Value readers.
   */
  private SpecificParquetRecordReaderBase.IntIterator repetitionLevelColumn;
  private SpecificParquetRecordReaderBase.IntIterator definitionLevelColumn;
  private ValuesReader dataColumn;

  // Only set if vectorized decoding is true. This is used instead of the row by row decoding
  // with `definitionLevelColumn`.
  private VectorizedRleValuesReader defColumn;

  /**
   * Total number of values in this column (in this row group).
   */
  private final long totalValueCount;

  /**
   * Total values in the current page.
   */
  private int pageValueCount;

  private final PageReader pageReader;
  private final ColumnDescriptor descriptor;
  private final OriginalType originalType;
  // The timezone conversion to apply to int96 timestamps. Null if no conversion.
  private final ZoneId convertTz;
  private static final ZoneId UTC = ZoneOffset.UTC;
  private final String datetimeRebaseMode;
  private final String int96RebaseMode;

  public VectorizedColumnReader(
      ColumnDescriptor descriptor,
      OriginalType originalType,
      PageReader pageReader,
      ZoneId convertTz,
      String datetimeRebaseMode,
      String int96RebaseMode) throws IOException {
    this.descriptor = descriptor;
    this.pageReader = pageReader;
    this.convertTz = convertTz;
    this.originalType = originalType;
    this.maxDefLevel = descriptor.getMaxDefinitionLevel();

    DictionaryPage dictionaryPage = pageReader.readDictionaryPage();
    if (dictionaryPage != null) {
      try {
        this.dictionary = dictionaryPage.getEncoding().initDictionary(descriptor, dictionaryPage);
        this.isCurrentPageDictionaryEncoded = true;
      } catch (IOException e) {
        throw new IOException("could not decode the dictionary for " + descriptor, e);
      }
    } else {
      this.dictionary = null;
      this.isCurrentPageDictionaryEncoded = false;
    }
    this.totalValueCount = pageReader.getTotalValueCount();
    if (totalValueCount == 0) {
      throw new IOException("totalValueCount == 0");
    }
    assert "LEGACY".equals(datetimeRebaseMode) || "EXCEPTION".equals(datetimeRebaseMode) ||
      "CORRECTED".equals(datetimeRebaseMode);
    this.datetimeRebaseMode = datetimeRebaseMode;
    assert "LEGACY".equals(int96RebaseMode) || "EXCEPTION".equals(int96RebaseMode) ||
      "CORRECTED".equals(int96RebaseMode);
    this.int96RebaseMode = int96RebaseMode;
  }

  /**
   * Advances to the next value. Returns true if the value is non-null.
   */
  private boolean next() throws IOException {
    if (valuesRead >= endOfPageValueCount) {
      if (valuesRead >= totalValueCount) {
        // How do we get here? Throw end of stream exception?
        return false;
      }
      readPage();
    }
    ++valuesRead;
    // TODO: Don't read for flat schemas
    //repetitionLevel = repetitionLevelColumn.nextInt();
    return definitionLevelColumn.nextInt() == maxDefLevel;
  }

  private boolean isLazyDecodingSupported(PrimitiveType.PrimitiveTypeName typeName) {
    boolean isSupported = false;
    switch (typeName) {
      case INT32:
        isSupported = originalType != OriginalType.DATE || "CORRECTED".equals(datetimeRebaseMode);
        break;
      case INT64:
        if (originalType == OriginalType.TIMESTAMP_MICROS) {
          isSupported = "CORRECTED".equals(datetimeRebaseMode);
        } else {
          isSupported = originalType != OriginalType.TIMESTAMP_MILLIS;
        }
        break;
      case FLOAT:
      case DOUBLE:
      case BINARY:
        isSupported = true;
        break;
    }
    return isSupported;
  }

  static int rebaseDays(int julianDays, final boolean failIfRebase) {
    if (failIfRebase) {
      if (julianDays < RebaseDateTime.lastSwitchJulianDay()) {
        throw DataSourceUtils.newRebaseExceptionInRead("Parquet");
      } else {
        return julianDays;
      }
    } else {
      return RebaseDateTime.rebaseJulianToGregorianDays(julianDays);
    }
  }

  private static long rebaseTimestamp(
      long julianMicros,
      final boolean failIfRebase,
      final String format) {
    if (failIfRebase) {
      if (julianMicros < RebaseDateTime.lastSwitchJulianTs()) {
        throw DataSourceUtils.newRebaseExceptionInRead(format);
      } else {
        return julianMicros;
      }
    } else {
      return RebaseDateTime.rebaseJulianToGregorianMicros(julianMicros);
    }
  }

  static long rebaseMicros(long julianMicros, final boolean failIfRebase) {
    return rebaseTimestamp(julianMicros, failIfRebase, "Parquet");
  }

  static long rebaseInt96(long julianMicros, final boolean failIfRebase) {
    return rebaseTimestamp(julianMicros, failIfRebase, "Parquet INT96");
  }

  /**
   * Reads `total` values from this columnReader into column.
   */
  void readBatch(int total, WritableColumnVector column) throws IOException {
    int rowId = 0;
    WritableColumnVector dictionaryIds = null;
    if (dictionary != null) {
      // SPARK-16334: We only maintain a single dictionary per row batch, so that it can be used to
      // decode all previous dictionary encoded pages if we ever encounter a non-dictionary encoded
      // page.
      dictionaryIds = column.reserveDictionaryIds(total);
    }
    while (total > 0) {
      // Compute the number of values we want to read in this page.
      int leftInPage = (int) (endOfPageValueCount - valuesRead);
      if (leftInPage == 0) {
        readPage();
        leftInPage = (int) (endOfPageValueCount - valuesRead);
      }
      int num = Math.min(total, leftInPage);
      PrimitiveType.PrimitiveTypeName typeName =
        descriptor.getPrimitiveType().getPrimitiveTypeName();
      if (isCurrentPageDictionaryEncoded) {
        // Read and decode dictionary ids.
        defColumn.readIntegers(
            num, dictionaryIds, column, rowId, maxDefLevel, (VectorizedValuesReader) dataColumn);

        // TIMESTAMP_MILLIS encoded as INT64 can't be lazily decoded as we need to post process
        // the values to add microseconds precision.
        if (column.hasDictionary() || (rowId == 0 && isLazyDecodingSupported(typeName))) {
          // Column vector supports lazy decoding of dictionary values so just set the dictionary.
          // We can't do this if rowId != 0 AND the column doesn't have a dictionary (i.e. some
          // non-dictionary encoded values have already been added).
          column.setDictionary(new ParquetDictionary(dictionary));
        } else {
          decodeDictionaryIds(rowId, num, column, dictionaryIds);
        }
      } else {
        if (column.hasDictionary() && rowId != 0) {
          // This batch already has dictionary encoded values but this new page is not. The batch
          // does not support a mix of dictionary and not so we will decode the dictionary.
          decodeDictionaryIds(0, rowId, column, column.getDictionaryIds());
        }
        column.setDictionary(null);
        switch (typeName) {
          case BOOLEAN:
            readBooleanBatch(rowId, num, column);
            break;
          case INT32:
            readIntBatch(rowId, num, column);
            break;
          case INT64:
            readLongBatch(rowId, num, column);
            break;
          case INT96:
            readBinaryBatch(rowId, num, column);
            break;
          case FLOAT:
            readFloatBatch(rowId, num, column);
            break;
          case DOUBLE:
            readDoubleBatch(rowId, num, column);
            break;
          case BINARY:
            readBinaryBatch(rowId, num, column);
            break;
          case FIXED_LEN_BYTE_ARRAY:
            readFixedLenByteArrayBatch(
              rowId, num, column, descriptor.getPrimitiveType().getTypeLength());
            break;
          default:
            throw new IOException("Unsupported type: " + typeName);
        }
      }

      valuesRead += num;
      rowId += num;
      total -= num;
    }
  }

  private boolean shouldConvertTimestamps() {
    return convertTz != null && !convertTz.equals(UTC);
  }

  /**
   * Helper function to construct exception for parquet schema mismatch.
   */
  private SchemaColumnConvertNotSupportedException constructConvertNotSupportedException(
      ColumnDescriptor descriptor,
      WritableColumnVector column) {
    return new SchemaColumnConvertNotSupportedException(
      Arrays.toString(descriptor.getPath()),
      descriptor.getPrimitiveType().getPrimitiveTypeName().toString(),
      column.dataType().catalogString());
  }

  /**
   * Reads `num` values into column, decoding the values from `dictionaryIds` and `dictionary`.
   */
  private void decodeDictionaryIds(
      int rowId,
      int num,
      WritableColumnVector column,
      WritableColumnVector dictionaryIds) {
    switch (descriptor.getPrimitiveType().getPrimitiveTypeName()) {
      case INT32:
        if (column.dataType() == DataTypes.IntegerType ||
            DecimalType.is32BitDecimalType(column.dataType()) ||
            (column.dataType() == DataTypes.DateType && "CORRECTED".equals(datetimeRebaseMode))) {
          for (int i = rowId; i < rowId + num; ++i) {
            if (!column.isNullAt(i)) {
              column.putInt(i, dictionary.decodeToInt(dictionaryIds.getDictId(i)));
            }
          }
        } else if (column.dataType() == DataTypes.ByteType) {
          for (int i = rowId; i < rowId + num; ++i) {
            if (!column.isNullAt(i)) {
              column.putByte(i, (byte) dictionary.decodeToInt(dictionaryIds.getDictId(i)));
            }
          }
        } else if (column.dataType() == DataTypes.ShortType) {
          for (int i = rowId; i < rowId + num; ++i) {
            if (!column.isNullAt(i)) {
              column.putShort(i, (short) dictionary.decodeToInt(dictionaryIds.getDictId(i)));
            }
          }
        } else if (column.dataType() == DataTypes.DateType) {
          final boolean failIfRebase = "EXCEPTION".equals(datetimeRebaseMode);
          for (int i = rowId; i < rowId + num; ++i) {
            if (!column.isNullAt(i)) {
              int julianDays = dictionary.decodeToInt(dictionaryIds.getDictId(i));
              column.putInt(i, rebaseDays(julianDays, failIfRebase));
            }
          }
        } else {
          throw constructConvertNotSupportedException(descriptor, column);
        }
        break;

      case INT64:
        if (column.dataType() == DataTypes.LongType ||
            DecimalType.is64BitDecimalType(column.dataType()) ||
            (originalType == OriginalType.TIMESTAMP_MICROS &&
              "CORRECTED".equals(datetimeRebaseMode))) {
          for (int i = rowId; i < rowId + num; ++i) {
            if (!column.isNullAt(i)) {
              column.putLong(i, dictionary.decodeToLong(dictionaryIds.getDictId(i)));
            }
          }
        } else if (originalType == OriginalType.TIMESTAMP_MILLIS) {
          if ("CORRECTED".equals(datetimeRebaseMode)) {
            for (int i = rowId; i < rowId + num; ++i) {
              if (!column.isNullAt(i)) {
                long gregorianMillis = dictionary.decodeToLong(dictionaryIds.getDictId(i));
                column.putLong(i, DateTimeUtils.millisToMicros(gregorianMillis));
              }
            }
          } else {
            final boolean failIfRebase = "EXCEPTION".equals(datetimeRebaseMode);
            for (int i = rowId; i < rowId + num; ++i) {
              if (!column.isNullAt(i)) {
                long julianMillis = dictionary.decodeToLong(dictionaryIds.getDictId(i));
                long julianMicros = DateTimeUtils.millisToMicros(julianMillis);
                column.putLong(i, rebaseMicros(julianMicros, failIfRebase));
              }
            }
          }
        } else if (originalType == OriginalType.TIMESTAMP_MICROS) {
          final boolean failIfRebase = "EXCEPTION".equals(datetimeRebaseMode);
          for (int i = rowId; i < rowId + num; ++i) {
            if (!column.isNullAt(i)) {
              long julianMicros = dictionary.decodeToLong(dictionaryIds.getDictId(i));
              column.putLong(i, rebaseMicros(julianMicros, failIfRebase));
            }
          }
        } else {
          throw constructConvertNotSupportedException(descriptor, column);
        }
        break;

      case FLOAT:
        for (int i = rowId; i < rowId + num; ++i) {
          if (!column.isNullAt(i)) {
            column.putFloat(i, dictionary.decodeToFloat(dictionaryIds.getDictId(i)));
          }
        }
        break;

      case DOUBLE:
        for (int i = rowId; i < rowId + num; ++i) {
          if (!column.isNullAt(i)) {
            column.putDouble(i, dictionary.decodeToDouble(dictionaryIds.getDictId(i)));
          }
        }
        break;
      case INT96:
        if (column.dataType() == DataTypes.TimestampType) {
          final boolean failIfRebase = "EXCEPTION".equals(int96RebaseMode);
          if (!shouldConvertTimestamps()) {
            if ("CORRECTED".equals(int96RebaseMode)) {
              for (int i = rowId; i < rowId + num; ++i) {
                if (!column.isNullAt(i)) {
                  Binary v = dictionary.decodeToBinary(dictionaryIds.getDictId(i));
                  column.putLong(i, ParquetRowConverter.binaryToSQLTimestamp(v));
                }
              }
            } else {
              for (int i = rowId; i < rowId + num; ++i) {
                if (!column.isNullAt(i)) {
                  Binary v = dictionary.decodeToBinary(dictionaryIds.getDictId(i));
                  long julianMicros = ParquetRowConverter.binaryToSQLTimestamp(v);
                  long gregorianMicros = rebaseInt96(julianMicros, failIfRebase);
                  column.putLong(i, gregorianMicros);
                }
              }
            }
          } else {
            if ("CORRECTED".equals(int96RebaseMode)) {
              for (int i = rowId; i < rowId + num; ++i) {
                if (!column.isNullAt(i)) {
                  Binary v = dictionary.decodeToBinary(dictionaryIds.getDictId(i));
                  long gregorianMicros = ParquetRowConverter.binaryToSQLTimestamp(v);
                  long adjTime = DateTimeUtils.convertTz(gregorianMicros, convertTz, UTC);
                  column.putLong(i, adjTime);
                }
              }
            } else {
              for (int i = rowId; i < rowId + num; ++i) {
                if (!column.isNullAt(i)) {
                  Binary v = dictionary.decodeToBinary(dictionaryIds.getDictId(i));
                  long julianMicros = ParquetRowConverter.binaryToSQLTimestamp(v);
                  long gregorianMicros = rebaseInt96(julianMicros, failIfRebase);
                  long adjTime = DateTimeUtils.convertTz(gregorianMicros, convertTz, UTC);
                  column.putLong(i, adjTime);
                }
              }
            }
          }
        } else {
          throw constructConvertNotSupportedException(descriptor, column);
        }
        break;
      case BINARY:
        // TODO: this is incredibly inefficient as it blows up the dictionary right here. We
        // need to do this better. We should probably add the dictionary data to the ColumnVector
        // and reuse it across batches. This should mean adding a ByteArray would just update
        // the length and offset.
        for (int i = rowId; i < rowId + num; ++i) {
          if (!column.isNullAt(i)) {
            Binary v = dictionary.decodeToBinary(dictionaryIds.getDictId(i));
            column.putByteArray(i, v.getBytes());
          }
        }
        break;
      case FIXED_LEN_BYTE_ARRAY:
        // DecimalType written in the legacy mode
        if (DecimalType.is32BitDecimalType(column.dataType())) {
          for (int i = rowId; i < rowId + num; ++i) {
            if (!column.isNullAt(i)) {
              Binary v = dictionary.decodeToBinary(dictionaryIds.getDictId(i));
              column.putInt(i, (int) ParquetRowConverter.binaryToUnscaledLong(v));
            }
          }
        } else if (DecimalType.is64BitDecimalType(column.dataType())) {
          for (int i = rowId; i < rowId + num; ++i) {
            if (!column.isNullAt(i)) {
              Binary v = dictionary.decodeToBinary(dictionaryIds.getDictId(i));
              column.putLong(i, ParquetRowConverter.binaryToUnscaledLong(v));
            }
          }
        } else if (DecimalType.isByteArrayDecimalType(column.dataType())) {
          for (int i = rowId; i < rowId + num; ++i) {
            if (!column.isNullAt(i)) {
              Binary v = dictionary.decodeToBinary(dictionaryIds.getDictId(i));
              column.putByteArray(i, v.getBytes());
            }
          }
        } else {
          throw constructConvertNotSupportedException(descriptor, column);
        }
        break;

      default:
        throw new UnsupportedOperationException(
          "Unsupported type: " + descriptor.getPrimitiveType().getPrimitiveTypeName());
    }
  }

  /**
   * For all the read*Batch functions, reads `num` values from this columnReader into column. It
   * is guaranteed that num is smaller than the number of values left in the current page.
   */

  private void readBooleanBatch(int rowId, int num, WritableColumnVector column)
      throws IOException {
    if (column.dataType() != DataTypes.BooleanType) {
      throw constructConvertNotSupportedException(descriptor, column);
    }
    defColumn.readBooleans(
        num, column, rowId, maxDefLevel, (VectorizedValuesReader) dataColumn);
  }

  private void readIntBatch(int rowId, int num, WritableColumnVector column) throws IOException {
    // This is where we implement support for the valid type conversions.
    // TODO: implement remaining type conversions
    if (column.dataType() == DataTypes.IntegerType ||
        DecimalType.is32BitDecimalType(column.dataType())) {
      defColumn.readIntegers(
          num, column, rowId, maxDefLevel, (VectorizedValuesReader) dataColumn);
    } else if (column.dataType() == DataTypes.ByteType) {
      defColumn.readBytes(
          num, column, rowId, maxDefLevel, (VectorizedValuesReader) dataColumn);
    } else if (column.dataType() == DataTypes.ShortType) {
      defColumn.readShorts(
          num, column, rowId, maxDefLevel, (VectorizedValuesReader) dataColumn);
    } else if (column.dataType() == DataTypes.DateType ) {
      if ("CORRECTED".equals(datetimeRebaseMode)) {
        defColumn.readIntegers(
          num, column, rowId, maxDefLevel, (VectorizedValuesReader) dataColumn);
      } else {
        boolean failIfRebase = "EXCEPTION".equals(datetimeRebaseMode);
        defColumn.readIntegersWithRebase(
          num, column, rowId, maxDefLevel, (VectorizedValuesReader) dataColumn, failIfRebase);
      }
    } else {
      throw constructConvertNotSupportedException(descriptor, column);
    }
  }

  private void readLongBatch(int rowId, int num, WritableColumnVector column) throws IOException {
    // This is where we implement support for the valid type conversions.
    if (column.dataType() == DataTypes.LongType ||
        DecimalType.is64BitDecimalType(column.dataType())) {
      defColumn.readLongs(
        num, column, rowId, maxDefLevel, (VectorizedValuesReader) dataColumn);
    } else if (originalType == OriginalType.TIMESTAMP_MICROS) {
      if ("CORRECTED".equals(datetimeRebaseMode)) {
        defColumn.readLongs(
          num, column, rowId, maxDefLevel, (VectorizedValuesReader) dataColumn);
      } else {
        boolean failIfRebase = "EXCEPTION".equals(datetimeRebaseMode);
        defColumn.readLongsWithRebase(
          num, column, rowId, maxDefLevel, (VectorizedValuesReader) dataColumn, failIfRebase);
      }
    } else if (originalType == OriginalType.TIMESTAMP_MILLIS) {
      if ("CORRECTED".equals(datetimeRebaseMode)) {
        for (int i = 0; i < num; i++) {
          if (defColumn.readInteger() == maxDefLevel) {
            column.putLong(rowId + i, DateTimeUtils.millisToMicros(dataColumn.readLong()));
          } else {
            column.putNull(rowId + i);
          }
        }
      } else {
        final boolean failIfRebase = "EXCEPTION".equals(datetimeRebaseMode);
        for (int i = 0; i < num; i++) {
          if (defColumn.readInteger() == maxDefLevel) {
            long julianMicros = DateTimeUtils.millisToMicros(dataColumn.readLong());
            column.putLong(rowId + i, rebaseMicros(julianMicros, failIfRebase));
          } else {
            column.putNull(rowId + i);
          }
        }
      }
    } else {
      throw constructConvertNotSupportedException(descriptor, column);
    }
  }

  private void readFloatBatch(int rowId, int num, WritableColumnVector column) throws IOException {
    // This is where we implement support for the valid type conversions.
    // TODO: support implicit cast to double?
    if (column.dataType() == DataTypes.FloatType) {
      defColumn.readFloats(
          num, column, rowId, maxDefLevel, (VectorizedValuesReader) dataColumn);
    } else {
      throw constructConvertNotSupportedException(descriptor, column);
    }
  }

  private void readDoubleBatch(int rowId, int num, WritableColumnVector column) throws IOException {
    // This is where we implement support for the valid type conversions.
    // TODO: implement remaining type conversions
    if (column.dataType() == DataTypes.DoubleType) {
      defColumn.readDoubles(
          num, column, rowId, maxDefLevel, (VectorizedValuesReader) dataColumn);
    } else {
      throw constructConvertNotSupportedException(descriptor, column);
    }
  }

  private void readBinaryBatch(int rowId, int num, WritableColumnVector column) throws IOException {
    // This is where we implement support for the valid type conversions.
    // TODO: implement remaining type conversions
    VectorizedValuesReader data = (VectorizedValuesReader) dataColumn;
    if (column.dataType() == DataTypes.StringType || column.dataType() == DataTypes.BinaryType
            || DecimalType.isByteArrayDecimalType(column.dataType())) {
      defColumn.readBinarys(num, column, rowId, maxDefLevel, data);
    } else if (column.dataType() == DataTypes.TimestampType) {
      final boolean failIfRebase = "EXCEPTION".equals(int96RebaseMode);
      if (!shouldConvertTimestamps()) {
        if ("CORRECTED".equals(int96RebaseMode)) {
          for (int i = 0; i < num; i++) {
            if (defColumn.readInteger() == maxDefLevel) {
              // Read 12 bytes for INT96
              long gregorianMicros = ParquetRowConverter.binaryToSQLTimestamp(data.readBinary(12));
              column.putLong(rowId + i, gregorianMicros);
            } else {
              column.putNull(rowId + i);
            }
          }
        } else {
          for (int i = 0; i < num; i++) {
            if (defColumn.readInteger() == maxDefLevel) {
              // Read 12 bytes for INT96
              long julianMicros = ParquetRowConverter.binaryToSQLTimestamp(data.readBinary(12));
              long gregorianMicros = rebaseInt96(julianMicros, failIfRebase);
              column.putLong(rowId + i, gregorianMicros);
            } else {
              column.putNull(rowId + i);
            }
          }
        }
      } else {
        if ("CORRECTED".equals(int96RebaseMode)) {
          for (int i = 0; i < num; i++) {
            if (defColumn.readInteger() == maxDefLevel) {
              // Read 12 bytes for INT96
              long gregorianMicros = ParquetRowConverter.binaryToSQLTimestamp(data.readBinary(12));
              long adjTime = DateTimeUtils.convertTz(gregorianMicros, convertTz, UTC);
              column.putLong(rowId + i, adjTime);
            } else {
              column.putNull(rowId + i);
            }
          }
        } else {
          for (int i = 0; i < num; i++) {
            if (defColumn.readInteger() == maxDefLevel) {
              // Read 12 bytes for INT96
              long julianMicros = ParquetRowConverter.binaryToSQLTimestamp(data.readBinary(12));
              long gregorianMicros = rebaseInt96(julianMicros, failIfRebase);
              long adjTime = DateTimeUtils.convertTz(gregorianMicros, convertTz, UTC);
              column.putLong(rowId + i, adjTime);
            } else {
              column.putNull(rowId + i);
            }
          }
        }
      }
    } else {
      throw constructConvertNotSupportedException(descriptor, column);
    }
  }

  private void readFixedLenByteArrayBatch(
      int rowId,
      int num,
      WritableColumnVector column,
      int arrayLen) {
    VectorizedValuesReader data = (VectorizedValuesReader) dataColumn;
    // This is where we implement support for the valid type conversions.
    // TODO: implement remaining type conversions
    if (DecimalType.is32BitDecimalType(column.dataType())) {
      for (int i = 0; i < num; i++) {
        if (defColumn.readInteger() == maxDefLevel) {
          column.putInt(rowId + i,
              (int) ParquetRowConverter.binaryToUnscaledLong(data.readBinary(arrayLen)));
        } else {
          column.putNull(rowId + i);
        }
      }
    } else if (DecimalType.is64BitDecimalType(column.dataType())) {
      for (int i = 0; i < num; i++) {
        if (defColumn.readInteger() == maxDefLevel) {
          column.putLong(rowId + i,
              ParquetRowConverter.binaryToUnscaledLong(data.readBinary(arrayLen)));
        } else {
          column.putNull(rowId + i);
        }
      }
    } else if (DecimalType.isByteArrayDecimalType(column.dataType())) {
      for (int i = 0; i < num; i++) {
        if (defColumn.readInteger() == maxDefLevel) {
          column.putByteArray(rowId + i, data.readBinary(arrayLen).getBytes());
        } else {
          column.putNull(rowId + i);
        }
      }
    } else {
      throw constructConvertNotSupportedException(descriptor, column);
    }
  }

  private void readPage() {
    DataPage page = pageReader.readPage();
    // TODO: Why is this a visitor?
    page.accept(new DataPage.Visitor<Void>() {
      @Override
      public Void visit(DataPageV1 dataPageV1) {
        try {
          readPageV1(dataPageV1);
          return null;
        } catch (IOException e) {
          throw new RuntimeException(e);
        }
      }

      @Override
      public Void visit(DataPageV2 dataPageV2) {
        try {
          readPageV2(dataPageV2);
          return null;
        } catch (IOException e) {
          throw new RuntimeException(e);
        }
      }
    });
  }

  private void initDataReader(Encoding dataEncoding, ByteBufferInputStream in) throws IOException {
    this.endOfPageValueCount = valuesRead + pageValueCount;
    if (dataEncoding.usesDictionary()) {
      this.dataColumn = null;
      if (dictionary == null) {
        throw new IOException(
            "could not read page in col " + descriptor +
                " as the dictionary was missing for encoding " + dataEncoding);
      }
      @SuppressWarnings("deprecation")
      Encoding plainDict = Encoding.PLAIN_DICTIONARY; // var to allow warning suppression
      if (dataEncoding != plainDict && dataEncoding != Encoding.RLE_DICTIONARY) {
        throw new UnsupportedOperationException("Unsupported encoding: " + dataEncoding);
      }
      this.dataColumn = new VectorizedRleValuesReader();
      this.isCurrentPageDictionaryEncoded = true;
    } else {
      if (dataEncoding != Encoding.PLAIN) {
        throw new UnsupportedOperationException("Unsupported encoding: " + dataEncoding);
      }
      this.dataColumn = new VectorizedPlainValuesReader();
      this.isCurrentPageDictionaryEncoded = false;
    }

    try {
      dataColumn.initFromPage(pageValueCount, in);
    } catch (IOException e) {
      throw new IOException("could not read page in col " + descriptor, e);
    }
  }

  private void readPageV1(DataPageV1 page) throws IOException {
    this.pageValueCount = page.getValueCount();
    ValuesReader rlReader = page.getRlEncoding().getValuesReader(descriptor, REPETITION_LEVEL);
    ValuesReader dlReader;

    // Initialize the decoders.
    if (page.getDlEncoding() != Encoding.RLE && descriptor.getMaxDefinitionLevel() != 0) {
      throw new UnsupportedOperationException("Unsupported encoding: " + page.getDlEncoding());
    }
    int bitWidth = BytesUtils.getWidthFromMaxInt(descriptor.getMaxDefinitionLevel());
    this.defColumn = new VectorizedRleValuesReader(bitWidth);
    dlReader = this.defColumn;
    this.repetitionLevelColumn = new ValuesReaderIntIterator(rlReader);
    this.definitionLevelColumn = new ValuesReaderIntIterator(dlReader);
    try {
      BytesInput bytes = page.getBytes();
      ByteBufferInputStream in = bytes.toInputStream();
      rlReader.initFromPage(pageValueCount, in);
      dlReader.initFromPage(pageValueCount, in);
      initDataReader(page.getValueEncoding(), in);
    } catch (IOException e) {
      throw new IOException("could not read page " + page + " in col " + descriptor, e);
    }
  }

  private void readPageV2(DataPageV2 page) throws IOException {
    this.pageValueCount = page.getValueCount();
    this.repetitionLevelColumn = createRLEIterator(descriptor.getMaxRepetitionLevel(),
        page.getRepetitionLevels(), descriptor);

    int bitWidth = BytesUtils.getWidthFromMaxInt(descriptor.getMaxDefinitionLevel());
    // do not read the length from the stream. v2 pages handle dividing the page bytes.
    this.defColumn = new VectorizedRleValuesReader(bitWidth, false);
    this.definitionLevelColumn = new ValuesReaderIntIterator(this.defColumn);
    this.defColumn.initFromPage(
        this.pageValueCount, page.getDefinitionLevels().toInputStream());
    try {
      initDataReader(page.getDataEncoding(), page.getData().toInputStream());
    } catch (IOException e) {
      throw new IOException("could not read page " + page + " in col " + descriptor, e);
    }
  }
}

redpajama_set_name: RedPajamaGithub


In accordance with the requirements of title II of the Americans with Disabilities Act of 1990 ("ADA"), the State University of New York – Morrisville State College ("SUNY Morrisville") will not discriminate against qualified individuals with disabilities on the basis of disability in its services, programs, or activities.
Employment: SUNY Morrisville does not discriminate on the basis of disability in its hiring or employment practices and complies with all regulations promulgated by the U.S. Equal Employment Opportunity Commission under title I of the ADA.
to people who have speech, hearing, or vision impairments.
Modifications to Policies and Procedures: SUNY Morrisville will make all reasonable modifications to policies and programs to ensure that people with disabilities have an equal opportunity to enjoy all of its programs, services, and activities, including online information and functionality. For example, individuals with service animals are welcomed in State University of New York offices, even where pets are generally prohibited.
Anyone who requires an auxiliary aid or service for effective communication, or a modification of policies or procedures to participate in a program, service, or activity should contact the office of SUNY Morrisville's ADA Coordinator as soon as possible but no later than 48 hours before the scheduled event.
The ADA does not require the SUNY Morrisville to take any action that would fundamentally alter the nature of its programs or services, or impose an undue financial or administrative burden.
SUNY Morrisville will not place a surcharge on a particular individual with a disability or any group of individuals with disabilities to cover the cost of providing auxiliary aids/services or reasonable modifications of policy, such as retrieving items from locations that are open to the public but are not accessible to persons who use wheelchairs.
Complaints/Grievances: Complaints that a program, service, or activity of State University of New York is not accessible to persons with disabilities should be directed to the appropriate ADA Coordinator, as set forth above.
In addition, the State University of New York has a Discrimination Complaint Procedure that may be utilized to file a complaint of unlawful discrimination on the basis of disability.
Download the Accessibility Policy as a PDF.


text:
Dubai tribunal upholds fines against Al Masah Capital and bans for three former executives
The individuals were found to be running funds from the DIFC without authorisation and to have misled investors over fees
The Financial Markets Tribunal upheld decisions taken by the Dubai Financial Services Authority in September last year to issue fines against two companies and ban three individuals who were running funds from within the DIFC without authorisation. Courtesy: DIFC Authority
Dubai's Financial Markets Tribunal upheld a Dubai Financial Services Authority ruling that imposed $4.5 million worth of fines against Al Masah Capital and banned three individuals that ran the company from offering financial services out of the Dubai International Financial Centre.
The tribunal upheld a $3m fine against Cayman Islands-based Al Masah Capital and a $1.5m fine against Al Masah Capital Management, which operated from the Dubai International Financial Centre. It also upheld fines of $225,000 against the company's former chief executive Shailesh Dash and a fine of $150,000 executive director Don Lim Jung Chiat. It increased a fine against chief financial officer Nrupaditya Singhdeo by $25,000 to $175,000. They were also ordered to pay the DFSA's legal costs.
Al Masah Capital to be liquidated following series of fines by Dubai regulator
Dubai Financial Services Authority fines two Abraaj companies Dh1.15 billion
Mr Dash did not respond to a request to comment when contacted by The National.
The DFSA originally issued enforcement notices against the two companies and the three individuals in September last year.
The case involved complicated structures in which investors bought shares in companies that then bought shares in other businesses.
However, the tribunal agreed with the DFSA that the arrangements were investments in funds and that these funds were being managed by Cayman Islands-based Al Masah Capital from within the DIFC, when it was not authorised to do so.
The tribunal also agreed that the two companies and three individuals had concealed from potential investors that placement fees paid to Al Masah Capital had been paid to a related party, and that the misrepresentation of fees was "intentional and deceptive", according to a DFSA statement on Wednesday.
"Where we see this conduct, we don't just take action against the firm, we take action against the responsible individuals and in particular the single individuals who are responsible," the DFSA's head of enforcement Patrick Meaney told The National.
Al Masah Capital was placed into liquidation in the Cayman Islands last month, meaning that the DFSA may not be able to recover the full amount of the fine imposed. However, the DFSA will be "a creditor in the insolvent estate and ... will also seek to recover against the DIFC entity", Mr Meaney said.
"But it's the individuals that matter at the end of the day and holding individuals accountable," he added.
"We expect that we will be able to recover the fines against the three individuals, but more importantly the prohibitions on them operating not only operate within the DIFC but send a message to any other regulatory body that these are not individuals that that they should be allowing to conduct financial services within their regulatory regimes," Mr Meaney added.
Peter Smith, the DFSA's head of strategy, policy and risk, said: "One of the key elements of taking action against firms is not just to punish those that have done wrong things, but also to get a message out that this sort of behaviour is unacceptable and won't be tolerated."
The DFSA remains on the lookout for businesses which structure their operations to give the appearance that they don't need to be authorised while carrying out operations from the DIFC, Mr Meaney said.
"Secondly, we view it as unacceptable for financial services firms to conceal or mislead investors in relation to information that is important for them making investment decisions."

redpajama_set_name: RedPajamaCommonCrawl


text:
Smarter is... a flexible vendor neutral managed service for City of Sydney
Written by Matt King
Business Development Director
11 July 2019 | 10 mins
It gives me great pleasure to announce City of Sydney Council is now live and utilising Comensura's adaptive, vendor neutral managed service to manage its temporary workforce.
Sydney is an iconic, vibrant, cosmopolitan city with a diverse population featuring a rich history, internationally recognised tourist attractions and an exciting calendar of events. Council is the local government authority responsible for the city centre and more than 30 suburbs within its boundaries.
The City of Sydney Council's Local Government Area (LGA) covers just over 26 square kilometres extending from Sydney Harbour at Rushcutters Bay, to Glebe and Annandale in the west, from Sydney Park and Rosebery in the south to Centennial Park and Paddington in the east. Council provide services for more than 200,000 residents and 20,000 businesses, as well as the daily influx of workers and visitors into the City. On any given day, the local population swells to more than 1 million with people commuting, doing business, shopping, playing, studying, or visiting the sights of Sydney.
Council engages temporary labour to supplement its permanent workforce to deliver specific projects, undertake specialist tasks and to backfill vacancies. Prior to partnering with Comensura, Council had engaged a broad range of contingent labour provided through a decentralised relationship-based arrangement with a large variety of recruitment agencies for the past 5 years. Throughout 2017 Council explored the managed service concept and came to market in July 2018 with a formal tender seeking a Vendor Neutral Managed Supplier for Contingent Labour services. Following an extensive evaluation process, interviews and Council approval, Comensura was delighted to be awarded the contract in December.
Comensura's unique Classic neutral vendor supply chain management solution provides the infrastructure, sourcing strategy and contract management expertise to effectively enable City of Sydney's hiring managers to interact simply with a compliant and specialist supply chain of recruitment agencies.
Our proprietary vendor management software, c.net provides the hiring community with centralised management and visibility of all non-permanent recruitment activity, giving Council access to real-time management information, improved governance of spend, rigorous compliance checks and customer service support.
Throughout the implementation period both Council and Comensura's project teams have been working in true partnership to ensure our solution met or exceeded key objectives include but not limited to the ability to provide a high-quality contingent labour force, to meet Council's diverse business needs at a price that represents value for money for Council's ratepayers.
In using c.net, City of Sydney benefit from a comprehensive reporting capability on all aspects of temporary labour hire acquisition, we also ensure all Council's compliance and risk management requirements are met and provide a high level of process efficiency in all transactions. A high-level candidate experience is also an integral part of this contract.
Comensura looks forward to our partnership with City of Sydney Council and delivering our progressive, innovative road map of continuous improvement through the life cycle of this landmark contract.
Contingent Labour
How useful did you find this article?
Back to blog listing
Related blog articles
Comensura TV!
We're delighted to announce that Comensura TV has now launched on Youtube!
New Contract Award!
We're hugely excited to announce that Comensura has been awarded a new long term contract by Portsmouth City Council.


crushing plant – Great Wall Heavy Industry Machinery Co.Ltd.
What are outstanding features of Great Wall Mobile crushers in Philippine mobile crushing plant ?
Mobile Crushing Plant Usage and Application: The mobile cone crushing plant is not limited by the crushing place, and reduces high material transportation cost.


text:
Up close and personal with Courage!
We can assure everyone that no photographer was harmed in the making of this outstanding shot! But the...
Silverstone implementing debris fencing changes for 2023
Silverstone is making changes to the placement of fencing along the Wellington Straight into Brooklands and Luffield, which...
F1i's Driver Ratings for the 2022 British GP
The British summer can always be relied upon to deliver just the sort of inclement conditions that throws a...
Team Talk - Sunday at Silverstone
Catch up with what the teams had to say following round 10 of the 2022 F1 World Championship, the...
2022 British Grand Prix Index
Welcome to our index page of the 2022 British Grand Prix. From here you will find links to...
Verstappen blames debris for costly 'rupture'
Max Verstappen looked to be on course for victory in today's British Grand Prix at Silverstone, having just...
Perez pulls off 'great comeback' to second at Silverstone
In what proved to be a stormy day for Red Bull, Sergio Perez succeeded in bouncing back from...
'A day I will not forget!' declares victorious Sainz
If claiming his maiden Formula 1 pole position in qualifying on Saturday represented a career high for Ferrari's...
Sainz wins drama-packed British GP in thrilling finish
A British Grand Prix packed with all sorts of drama and excitement saw Ferrari's Carlos Sainz emerge victorious...
2022 British Grand Prix - Race results
Full race results from the British Grand Prix at Silverstone, round 10 of the 2022 Formula 1 season....
Norris happy with P6 on the grid despite 'messing up'
McLaren's Lando Norris insisted that he was happy with sixth place on the grid for the British GP...
Binotto admits concern at Ferrari over reliability
Ferrari team principal Mattia Binotto admitted there is concern about the reliability of the team's cars heading into...
1 2 … 23 ▶


text:
package com.github.mefi.jkuuza.analyzer;

import com.github.mefi.jkuuza.model.BasicProductProperties;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * @author Marek Pilecky
 */
public class Rules {

	Map<String, String> map;

	public Rules() {
		map = new HashMap<String, String>();
	}

	/**
	 * Add property and its selector
	 *
	 * @param property
	 * @param selector
	 */
	public void add(String property, String selector) {
		map.put(property, selector);
	}

	/**
	 * Add property and its selector
	 *
	 * @param property
	 * @param selector
	 */
	public void add(BasicProductProperties property, String selector) {
		map.put(property.toString(), selector);
	}

	/**
	 * Return selector by property name
	 *
	 * @param property
	 * @return selector or empty string if property dosn't exists
	 */
	public String getSelector(String property) {
		if (map.get(property) == null) {
			return "";
		}
		return map.get(property);
	}

	/**
	 * Return selector by property name
	 *
	 * @param property
	 * @return selector or empty string if property dosn't exists
	 */
	public String getSelector(BasicProductProperties property) {
		return getSelector(property.toString());
	}

	/**
	 * Returns map with rules values
	 *
	 * @return map where key is property name and value is selector
	 */
	public Map<String, String> getValues() {
		return map;
	}
}


text:
Longtime board member resigns
Zac Nees. Photo by Chris Stanley
By Michael Rocco |
PUBLISHED: March 14, 2003 at 5:38 a.m. | UPDATED: September 24, 2021 at 11:12 a.m.
Kranz's seat is the third that the school board will fill in three years.
In his letter, read by board President Richard Swartley, Kranz wrote that he retired last year and has taken an opportunity to work part time and will be out of town in the spring and fall. Kranz, who was in his 12th year on the board, was not at Thursday's meeting.
Kranz was one of six incumbents up for election this year who was not seeking re-election. The board will have 30 days to fill the seat. The interviews will be public.
Because his seat is up for election, candidates for the vacancy will likely be two men running for the school board, Mark Leer of Franconia and Jack Parry of Souderton.
"It does not make any sense to look for any other candidate because they would be out as of December," Swartley said.
Kranz said last week that he was proud of making the meetings more public and putting maintenance practices in place for district buildings.
"He will be missed," Swartley said.
Michael Rocco


text:
MCP DOUBLE REVIEW- 1942: JOINT STRIKE AND COMMANDO 3
Alex Riviello
Master Control Program
DOWNLOAD THEM FROM XBLA OR PSN FOR: 10 Bucks/800 Points
PLATFORM: Xbox 360, PS3
ESRB RATING: T
"Two classic shooters reborn! Well, one classic shooter and one rightfully forgotten one!"
Consider these sequels to those old games. 1942 is much more faithful to the original 194X series, while Commando 3 is more of a revamp. Both are done by Backbone Entertainment and it shows, as the execution of both are remarkably similar.
There's basically nothing in the way of story for either title, and there doesn't need to be, if they were well done shooters. But are they?
1942: Joint Strike-
This game controls and plays exactly as it should… you shoot your way through wave after wave of enemy planes and try to avoid their bullets. You can shoot bullets, missiles, and a big bomb that clears the screen of enemies. You get a multiplier bonus for getting close to enemies as you shoot them- up to a 16X combo. At the end of every stage there's a big boss battle. But the basic tactic is- keep shooting! Be prepared to wear your right thumb down from jamming the button…
It really is an old school shooter through and through. It's all about repetition and memorizing the enemies' attack patterns, which should determine if this would be fun for you at all. It's not terribly difficult but it'll take you at least a few playthroughs to get by all the levels. (of which there are only 5). The enemies range from simple ships to the expected gigantic metal behemoths that take up half the screen and fill up the other half with bullets. But even at the hardest difficulty there should be no problem getting to the end; all the harder difficulties do is limit the amount of lives you have. Somewhat disappointing.
2 player is fun (you can perform that titular Joint Strike together for massive damage), but as this is the kind of game that takes hard work to get through, don't expect to have your buddy just jump in it with you and survive. It's only going to be fun if you have another hardcore shooter fan to play with, or find someone online.
It's interesting that the last shooter released for XBLA is a perfect example of the way shooters have changed over the years (evolved, if you will). Each time you boot that game up you have no idea what you're in for. With random enemies appearing and behaving differently each time it allows you to play on a much twitchier level. You have to feel the game, be a part of it. With a shooter like 1942 you have to dominate it, to learn its tricks and beat it down into submission. I'm beginning to much prefer the new type of shooters but there's still a large place in my heart for the old school. Fun for those who put the time into it, even if there's not much to it.
Wolf of the Battlefield: Commando 3:
This is a similar shooter, but it's lacking in fun. The game is done in a cartoony style, as seems to be all the rage these days. You've got 3 characters to play as, which feels like a real throwback. You've got the average All-American soldier, the big burly hairy guy with lots of health, and the fast and weak female. All control the same way, which are more of what you'd expect from a downloadable shooter- you use the left analog stick to move and right stick to shoot. The right trigger throws grenades and the left is a screen-clearing special move.
The aspect of the game that keeps it from being fun? The enemies are C-H-E-A-P. How cheap? Well, they can shoot through obstacles that you can't, their bullets travel much farther (and are hidden away by particle effects in some levels- I'm looking at you, Level 2) and they respawn out of nowhere. So many times you'll clear a dead end and turn around to go back to the main path only to get shot in the back. It's pretty infuriating.
So much so that you're more likely to not want to try and beat the game again. There's nothing worse than a shooter that kills you for things you can't control.
The environments in both games are very bland, and it's rare when you run across a building or structure that's destructible. Not much interaction plus not much going on in the background makes for some boring stages. The characters in Commando might be well animated for all I know, but they're so tiny you can hardly see them.
Both games have the exact same menu system. They also share some very mediocre and bland music, unfortunately. Here's a tip- pick up the absolutely amazing Bionic Commando Rearmed Soundtrack and pipe it in through your system- it works much, much better.
THE REPLAY
Shooters are built around replay value, and these don't really disappoint in that regard. It won't take you long to beat either one once you've got the nuances down (probably about a half hour hour each from start to finish) but it'll take you hours to get there in the first place. As long as you don't mind playing the same thing over and over you'll have plenty of replay here.
The online and offline multiplayer helps keep things fresh, as well. Commando 3 supports an extra player over 1942 (3 players!), but of course it's not nearly as fun.
Of the two, 1942 is the clear victor here, but there could have been so much more done for these games. With so many great shooters already available for download (and a new Bionic Commando just released) these are really only recommended for fans of the originals.
1942: Joint Strike- 7.5 out of 10
Wolf of the Battlefield- Commando 3- 5.5 out of 10
Creature Corner


text:
Experts Say You Can Keep These 10 Thoughts To Yourself In Relationships
Some things are better left unsaid.
by Carolyn Steber and Mia Sherin
Addictive Stock / Rafa Cortes/Addictive Stock/Getty Images
Nine times out of 10, you're going to want to be open with your partner and let them know what you're thinking, feeling, hoping for, and so on. Communication is, after all, one of the most important factors in maintaining a relationship. But should you tell your partner everything? Experts agree that you don't have to.
"I absolutely think that is not only normal, not only OK, but really great to have some private thoughts or things in your life that are just yours," dating and relationship expert Cora Boyd tells Bustle. "And I don't think that has to exist in the same space [as] deceit or omission whatsoever."
In fact, keeping a few thoughts to yourself can be beneficial at times, especially if they won't contribute to your relationship in a positive way. "Total honesty isn't always the best policy," Jonathan Bennett, relationship and dating expert at Double Trust Dating, tells Bustle. If revealing this information will serve no real purpose, or if it will cause hurt feelings, he says, then it may be something worth keeping to yourself.
It'll be up to you to judge what needs to be said and what's OK to keep quiet. You may find that "some things are best kept private in order to spare others pain and keep peace in the relationship," Bennett says. As well as respecting boundaries and sparing feelings, keeping some thoughts private can also help you maintain an individual identity while in a relationship. "It's really, really important for the health of the relationship to continue to nurture and have a connection with your own individuality," Boyd says. This can come with breaking the notion that you have to tell your partner everything.
Of course, that's not to say you shouldn't discuss tough subjects or have deep conversations about whatever's going on in your relationship. Open communication is undoubtedly the key ingredient to a healthy partnership, and you should never be hiding information or starting sentences with, "Don't tell my boyfriend, but..." (If you find yourself prefacing convos with your friends like this, it might be time to reevaluate some things in your relationship.) Every couple is different, meaning that each pair will have their own communication styles that feel comfortable for them. So, while this is by no means a definite list, you may want to avoid some of the topics below in the interest of maintaining a harmonious connection.
Small Quirks You Find Annoying
Delmaine Donson/E+/Getty Images
"When you're around someone all the time, it's easy to notice that person's flaws and imperfections," Bennett says. You might feel irritated by little things your partner does throughout the day or zero in on their small quirks. "But if you're happy and the relationship is solid, it's not worth focusing on the little things that bother you," he says. "It's better to keep them a secret than to make your partner feel insecure or hurt" by pointing them out.
While it's always great to be open with your partner if any of their actions are hurting you or your feelings, chances are their loud chewing or quirky laugh isn't bringing anyone harm. And, as Boyd says, "Criticism erodes connection. So, we want to be selective and intentional with giving feedback." You can try to bring up certain issues in a positive way, especially if the quirk is causing problems. But the reality is everyone has a "flaw" or two, and many can't be helped or changed.
Leftover Feelings You Have For An Ex
"Complicated feelings for exes are normal," Bennett says. You might still be recovering from the end of that previous relationship. Or you might wonder, for brief moments, what your ex is up to.
But is that something you need to say out loud to your current partner? Maybe not. "Even in the most secure of relationships, it is not always necessary to make your ex present in the room," Dr. Racine Henry, Ph.D., LMFT, licensed marriage and family therapist and owner of Sankofa Marriage and Family Therapy, tells Bustle. "That person is from your past, and you can discuss their new job, marriage, [or] haircut with your close friends who will be better able to keep things in perspective."
It's also important to recognize that there is a difference between sharing helpful context about your prior relationship and emotionally processing this past with your partner. "It's not appropriate and is actually really unhelpful to do emotional processing about your ex with your current partner," Boyd says. If you are considering talking about your ex, try to decipher if this is simply a "relaying of information, versus a processing of your thoughts, narratives, and feelings around your previous relationships," Boyd explains.
How You Feel About Their Family
Unless the family is creating some sort of toxic situation — in which case you should speak up and let your partner know — there's really no need to share minor things you dislike about your partner's loved ones. After all, "getting into a serious relationship with another person means you also enter into their world," Bennett says. And that includes spending time with potentially annoying little cousins or aunts who always say the wrong thing.
"For the sake of a peaceful relationship, unless the family causes a major issue, it's best to keep your thoughts to yourself," Bennett suggests. You can joke and laugh about it with your partner, but you may want to tread lightly and try not to say anything insulting.
That You Find A Friend Attractive
"Having an attraction towards another person is a normal human response and is nothing to be ashamed of," Michelle Fraley, MA, WPCC, psychologist, relationship expert, and professional matchmaker, tells Bustle. "However, telling your partner about this attraction will most likely only result in hurt feelings, jealousy, insecurity, and awkwardness."
For some couples, it may be common practice to tell each other when you think another person is good looking — regardless of their gender, we can all appreciate an attractive human every once in a while. But it's important to keep the intent in mind when sharing this sort of information. Boyd says, "Are you sharing it from a place of, 'So and so's a handsome dude,' versus 'I'm really attracted to your friend'?" If it's the latter, then it's probably best to keep those thoughts private. But either way, it may be a safer bet to not make a habit of telling your partner about people you've got heart eyes for. As Boyd says, those thoughts are "none of their business."
If you happen to have a little mini crush, that's fine, but it's not necessary to tell your partner. As long as you aren't acting on your thoughts, and they aren't impacting your relationship in any way, you may be better off keeping them to yourself.
That You Have Fleeting Doubts About Your Relationship
"Words are powerful weapons, and once they are said, they cannot be taken back," Fraley says. So unless you really and truly mean it, never say out loud that you're having doubts about the relationship.
In fact, "throughout a relationship, it is normal to have thoughts and feelings about your connection and longevity," Fraley says. "These thoughts may be fleeting, so it would be wise to sit on them and do some internal work before sharing such powerful (and potentially hurtful) thoughts with your partner."
And you'll never want to say anything like this in the heat of the moment during an argument. While you may be angry and upset, saying aloud that you're having doubts can be a tough thing to recover from. It may be tempting to feel guilty for even having these thoughts, but try to relieve yourself from any sort of shame. "You are not your thoughts," Boyd says. "Even if you sometimes imagine the prospect of breaking up, [that] doesn't mean that is what you want. It's literally just a thought." It's only natural for our minds to wander, so it's best to not take every thought that crosses our minds as the truth.
Unsolicited Opinions About Their Friends
As with family, it's not always wise to share exactly what you're thinking about your partner's friends. "Unless this person is doing something to you that is offensive, you should keep your opinions [...] to yourself," Henry says.
You can also be perfectly honest about things you don't like without it going down a toxic road. For example, "if your partner asks your thoughts about their friendships or familial relationships, tread lightly and do more listening than speaking," Henry suggests. Approach the conversation with the goal of supporting your partner rather than leading with your personal feelings.
How You Feel About Their Goals
Sharing your life with someone means you get to talk about each other's plans for the future, including your individual goals and how they might impact you as a couple. And yet, that doesn't mean you get to rush each other or put unnecessary pressure on your partner to follow a specific timeline.
"There is nothing wrong with holding them accountable if they are receptive to your involvement, but overall, working towards one's goals is a personal and often vulnerable journey,' Henry says. "You can be supportive while also respecting their individual process, even if it seems like they are doing things the hard way."
Your Family's Opinion About Your Partner
"In an ideal world, our family and friends would feel positively about our partners and see them in the same light as we do," Henry says. "Realistically, that doesn't always happen." Even if your family has a thing or two to say, that doesn't mean it needs to get back to your partner, especially since these words can be difficult to forget. The memory of an offensive comment may stick around long after the family dynamic shifts — so even if your family grows to love and support your relationship, that initial hurt won't fade easily.
"The people in your life should always respect your partner and your decision to be with them," Henry says. So while you're busy shielding your partner, you may want to stand up for your partner, too. It's perfectly reasonable for your family to be concerned for your well-being, but they should also trust you and your ability to make choices for yourself.
Things You Miss About Old Relationships
"Our minds work 24/7 at processing our lives, and this includes previous partners [and] comparisons with your current relationship, especially in newer relationships," Jorge Fernandez, LCSW, an individual and family psychotherapist, tells Bustle. "This is absolutely normal and doesn't indicate any sort of dissatisfaction with your current partner."
And yet, as you might have guessed, these aren't things you'll necessarily want to say out loud to your partner. Instead, try to find a way to incorporate anything you miss — like traditions or adventures — into your current life. And if you're still hung up on the past, Fernandez suggests talking to a therapist.
Details From Your Romantic History
Not only do you not need to share things you miss about your previous relationships, but any of the nitty gritty details about your dating history are up to your discretion. Whether that be information about your past sexual partners, your worst breakups, or the kinky activities you've tried in bed, you may want to take a beat before sharing that with your current partner.
Boyd suggests "just being judicious and intentional with commentary about your romantic history." This means checking in with yourself and your intentions before bringing up an aspect of your dating, relationship, or sexual past. "It can provide context and help them understand you and know you better as a person," Boyd explains. But on the flip side, disclosing your romantic history could be used "as a manipulative tactic to elicit jealousy." Simply be mindful of your intent, and remember that it's OK if you'd like to keep any of this information private.
You don't have to divulge every thought in order to have a strong relationship, especially when that thought might do more harm than good. So take your partner and your connection into consideration, and know that it's always OK to keep a few things to yourself.
Cora Boyd, dating and relationship expert
Jonathan Bennett, relationship and dating expert
Dr. Racine Henry, PhD, LMFT, licensed marriage and family therapist and owner of Sankofa Marriage and Family Therapy
Michelle Fraley, MA, WPCC, psychologist, relationship expert, and professional matchmaker
Jorge Fernandez, LCSW, an individual and family psychotherapist
12 Active Date Ideas That'll Get You & Your Boo Outside
By Lexi Inks
TikTok's Voluntary Celibacy Trend Has Sparked A Movement
This article was originally published on June 26, 2019


text:
Shoshana Kaufman
I Just Realized the Truth About American Healthcare
Actually applying for it was a shocking reality check — I have always known something about the reality of American healthcare versus Canadian healthcare and have always felt very smug about living in Canada and not having to put up with the sordidness of the American private healthcare insurance system. It is all over the news and the internet and…
Samuel O'Brient
·8 hours ago
The Future is Still Moderate — Why Democrats Should Run Buttigieg in 2024
Democrats need to forget about Bernie Sanders and focus on Mayor Pete. — We're barely a month into 2022 and many eyes are already cast toward 2024. The increasingly important 2022 midterm elections are approaching much sooner but most voters are more concerned with who is going to challenge Donald Trump in 2024. That said, discussion of that particular topic is only going…
ScottCDunn
·1 day ago
It's Harder Than Ever To Vote By Mail In Texas
An administrative procedure with no administrative remedy? Sounds like voter suppression. — Texas is working hard to reduce voter turnout across its valiant and glorious state. New laws that come into effect this year include tighter regulations for people who desire to vote by mail. Apparently, they've taken the words of former President Trump to heart, and they're very concerned about voter…
Congress Needs To Stop Day Trading, Says Sen. Mark Warner
"If you take these jobs of responsibility, you have to be willing to give up something." — Mark Warner has a different background than his colleagues in the Senate, one more common in Silicon Valley than Washington's halls. Before Virginians elected him U.S. Senator in 2008, and governor six years before that, Warner was a venture capitalist and entrepreneur. He co-founded Nextel, a wireless company now owned…
Micah Sifry
Who is more interested in rigging elections, Senator McConnell?
What a close look at the Senate's leading obstructionist shows about the voting rights debate — According to Senator Mitch McConnell (R-KY), the leader of the Senate Republican minority, the two bills that Democrats are now pushing to address voting rights are a "bald-faced attempt by liberals to rig our electoral systems in their own favor." …
Yes, the Biden Boom Is Real. We Shouldn't Ignore It.
There's more to the current economy than inflation — "The economy, stupid." The phrase, iconically coined by James Carville, was made famous during Bill Clinton's time in the White House. Since then, it's been used often, to the point that it's almost become a meme. During Donald Trump's turbulent presidency, we had plenty of reasons to reference it but…
Joseph Dana
China's Pull Grows in the Middle East
The key to China's geopolitical success continues to be infrastructure development — China is kicking off 2022 with a flurry of diplomacy and partnerships in Africa and the Middle East that are ruffling some feathers. Chinese Foreign Minister Wang Yi has just finished a trip to East Africa on a charm offensive designed to shore up China's presence on the Red Sea…
Eric Weiner
The Media Is Biased, But Not in the Way You Think
News outlets fixate on the negative — The critics are right. The mainstream media is biased. It is not a political bias, though, no liberal or conservative slant, but something even more insidious: a bad-news bias. During my decades as a daily journalist, at The New York Times and NPR, I knew that reporting on happy people…
Andrew McWilliams-Doty
Unlocking the Manchin Vote
The central question is: What makes Manchin tick? — Few challenges have been more vexing for the Democratic Party of the 117th Congress than unlocking Sen. Joe Manchin's vote for progressive legislation. From casting the 50th vote for last spring's $1.9T emergency COVID funding to blowing up the Build Back Better Act in December, Manchin has repeatedly been the…
Gwen Frisbie-Fulton
Ten Years Since Occupy and the Need Is Higher Than Ever
Occupy Greensboro was packing up. Tents were taken down, cooking pots stacked, banners folded. I loaded a number of large white boards into the back of my hatchback. …
Sign up for Flux
By GEN
A weekly newsletter about the powerful forces reshaping America. Take a look.
Get this newsletter
By signing up, you will create a Medium account if you don't already have one. Review our Privacy Policy for more information about our privacy practices.
Medium sent you an email at to complete your subscription.


Superheroes and supervillains have the best hangouts – secret bases, headquarters, fortresses, caves and lairs. These bases are filled with incredible treasures and trophies, guarded by cutting edge traps and guardians. Doesn't that sound like a dungeon to you? What good is a dungeon if you can't get your friends and crawl it, looting it of its goodies while overcoming its challenges? It's no good to anyone, except its actual owner. So, in order to make a great campaign setting of superpowered dungeon crawling, we removed all the previous owners of these bases (obviously by a massive cosmic/crossover event) so you could become a base raider!
Base Raiders is a new setting usable for any role playing game that allows superpowers for player characters. It is based in part on material from the Heroes of New Arcadia, a Wild Talents campaign on the Role Playing Public Radio Actual Play Podcast. The setting was created and written by Ross Payton.