<commit_before>// Copyright 2019 DeepMind Technologies Ltd. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "open_spiel/games/othello.h"

#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
#include <tuple>

#include "open_spiel/spiel_utils.h"
#include "open_spiel/utils/tensor_view.h"

namespace open_spiel {
namespace othello {
namespace {

// Facts about the game.
const GameType kGameType{
    /*short_name=*/"othello",
    /*long_name=*/"Othello",
    GameType::Dynamics::kSequential,
    GameType::ChanceMode::kDeterministic,
    GameType::Information::kPerfectInformation,
    GameType::Utility::kZeroSum,
    GameType::RewardModel::kTerminal,
    /*max_num_players=*/2,
    /*min_num_players=*/2,
    /*provides_information_state_string=*/true,
    /*provides_information_state_tensor=*/false,
    /*provides_observation_string=*/true,
    /*provides_observation_tensor=*/true,
    /*parameter_specification=*/{}  // no parameters
};

std::shared_ptr<const Game> Factory(const GameParameters& params) {
  return std::shared_ptr<const Game>(new OthelloGame(params));
}

REGISTER_SPIEL_GAME(kGameType, Factory);

}  // namespace

std::tuple<int, int> GetNext(int row, int col, Direction dir) {
  switch (dir) {
    case Direction::kUp:
      return std::make_tuple(row - 1, col);
    case Direction::kDown:
      return std::make_tuple(row + 1, col);
    case Direction::kLeft:
      return std::make_tuple(row, col - 1);
    case Direction::kRight:
      return std::make_tuple(row, col + 1);
    case Direction::kUpRight:
      return std::make_tuple(row - 1, col + 1);
    case Direction::kUpLeft:
      return std::make_tuple(row - 1, col - 1);
    case Direction::kDownRight:
      return std::make_tuple(row + 1, col + 1);
    case Direction::kDownLeft:
      return std::make_tuple(row + 1, col - 1);
    default:
      SpielFatalError(absl::StrCat("Found unmatched case in GetNext."));
  }
}

inline bool OthelloState::OnBoard(int row, int col) const {
  return (0 <= row) && (row < kNumRows) && (0 <= col) && (col < kNumCols);
}

int OthelloState::CountSteps(Player player, int row, int col, Direction dir) const {
  std::tuple<int, int> rowcol = GetNext(row, col, dir);
  row = std::get<0>(rowcol);
  col = std::get<1>(rowcol);

  int count = 0;
  CellState cell = PlayerToState(player);
  while (OnBoard(row, col)) {
    if (BoardAt(row, col) == cell) {
      return count;
    } else if (BoardAt(row, col) == CellState::kEmpty) {
      return 0;
    }

    count++;
    rowcol = GetNext(row, col, dir);
    row = std::get<0>(rowcol);
    col = std::get<1>(rowcol);
  }

  return 0;
}

bool OthelloState::CanCapture(Player player, int move) const {
  if (board_[move] != CellState::kEmpty) return false;

  std::tuple<int, int> row_col = XYFromCode(move);
  int row = std::get<0>(row_col);
  int col = std::get<1>(row_col);

  // bool none_adjacent = true;
  // for (int i = -1; i < 2; i++) {
  //   for (int j = -1; j < 2; j++) {
  //     if (OnBoard(row + i, col + j) && (BoardAt(row + i, col + j) != CellState::kEmpty)) none_adjacent = false;
  //   }
  // }

  // if (none_adjacent) return false;

  bool valid = false;
  for (int direction = Direction::kUp; direction < Direction::kLast; direction++) {
    if (CountSteps(player, row, col, static_cast<Direction>(direction)) != 0) {
      return true;
    }
  }

  return false;
}

void OthelloState::Capture(Player player, int row, int col, Direction dir, int steps) {
  std::tuple<int, int> rowcol = GetNext(row, col, dir);
  row = std::get<0>(rowcol);
  col = std::get<1>(rowcol);
  CellState cell = PlayerToState(player);

  for (int step = 0; step < steps; step++) {
    if (BoardAt(row, col) == CellState::kEmpty || BoardAt(row, col) == cell) {
      SpielFatalError(absl::StrCat("Cannot capture cell (", row, ", ", col, ")"));
    }

    board_[row * kNumCols + col] = cell;

    rowcol = GetNext(row, col, dir);
    row = std::get<0>(rowcol);
    col = std::get<1>(rowcol);
  }
}

int OthelloState::DiskCount(Player player) const {
  int count = 0;
  CellState cell = PlayerToState(player);
  for (int i = 0; i < kNumCells; i++) {
    if (board_[i] == cell) count++;
  }

  return count;
}

bool OthelloState::NoValidActions() const {
  return (LegalRegularActions(Player(0)).empty() & LegalRegularActions(Player(1)).empty());
}

std::tuple<int, int> OthelloState::XYFromCode(int move) const {
  if (move >= kNumCells || move < 0) {
    SpielFatalError(absl::StrCat("Move too large: ", move));
  }

  return std::make_tuple(move / kNumCols, move % kNumRows);
}

CellState PlayerToState(Player player) {
  switch (player) {
    case 0:
      return CellState::kBlack;
    case 1:
      return CellState::kWhite;
    default:
      SpielFatalError(absl::StrCat("Invalid player id ", player));
      return CellState::kEmpty;
  }
}

std::string StateToString(CellState state) {
  switch (state) {
    case CellState::kEmpty:
      return ".";
    case CellState::kWhite:
      return "o";
    case CellState::kBlack:
      return "x";
    default:
      SpielFatalError("Unknown state.");
  }
}

bool OthelloState::ValidAction(Player player, int move) const {
  return (board_[move] == CellState::kEmpty && CanCapture(player, move));
}

void OthelloState::DoApplyAction(Action move) {
  if (move == passMove) {  // pass
    current_player_ = 1 - current_player_;
    num_moves_ += 1;
    return;
  }

  if (!ValidAction(current_player_, move)) {
    SpielFatalError(absl::StrCat("Invalid move ", move));
  }

  std::tuple<int, int> rowcol = XYFromCode(move);
  CellState cell = PlayerToState(current_player_);

  int row = std::get<0>(rowcol);
  int col = std::get<1>(rowcol);
  board_[row * kNumCols + col] = cell;

  for (int direction = Direction::kUp; direction < Direction::kLast; direction++) {
    int steps = 0;
    if ((steps = CountSteps(current_player_, row, col, static_cast<Direction>(direction))) > 0) {
      Capture(current_player_, row, col, static_cast<Direction>(direction), steps);
    }
  }

  if (NoValidActions()) {
    int count_zero = DiskCount(Player(0));
    int count_one = DiskCount(Player(1));

    if (count_zero > count_one) {
      outcome_ = Player(0);
    } else if (count_zero < count_one) {
      outcome_ = Player(1);
    }
  }

  current_player_ = 1 - current_player_;
  num_moves_ += 1;
}

std::vector<Action> OthelloState::LegalRegularActions(Player p) const {  // list
  std::vector<Action> moves;
  for (int cell = 0; cell < kNumCells; ++cell) {
    if (ValidAction(p, cell)) {
      moves.push_back(cell);
    }
  }

  return moves;
}

std::vector<Action> OthelloState::LegalActions() const {
  if (IsTerminal()) return {};

  // can move in an empty cell that captures
  std::vector<Action> moves = LegalRegularActions(current_player_);
  if (moves.empty()) {
    moves.push_back(passMove);  // pass
  }

  return moves;
}

std::string OthelloState::ActionToString(Player player,
                                           Action action_id) const {

  if (action_id == passMove) {
    return absl::StrCat(StateToString(PlayerToState(player)), "(pass)");
  } else {
    return absl::StrCat(StateToString(PlayerToState(player)), "(",
                      action_id / kNumCols, ",", action_id % kNumCols, ")");
  }
}

bool OthelloState::IsFull() const {
  for (int i = 0; i < kNumCells; i++) {
    if (board_[i] == CellState::kEmpty) return false;
  }

  return true;
}

OthelloState::OthelloState(std::shared_ptr<const Game> game) : State(game) {
  std::fill(begin(board_), end(board_), CellState::kEmpty);
  board_[27] = CellState::kWhite;
  board_[28] = CellState::kBlack;
  board_[35] = CellState::kBlack;
  board_[36] = CellState::kWhite;
}

std::string OthelloState::ToString() const {
  std::string str;
  for (int r = 0; r < kNumRows; ++r) {
    for (int c = 0; c < kNumCols; ++c) {
      absl::StrAppend(&str, StateToString(BoardAt(r, c)));
    }
    if (r < (kNumRows - 1)) {
      absl::StrAppend(&str, "\n");
    }
  }
  return str;
}

bool OthelloState::IsTerminal() const {
  return outcome_ != kInvalidPlayer || NoValidActions();
}

std::vector<double> OthelloState::Returns() const {
  if (outcome_ == Player{0}) {
    return {1.0, -1.0};
  } else if (outcome_ == Player{1}) {
    return {-1.0, 1.0};
  } else {
    return {0.0, 0.0};
  }
}

std::string OthelloState::InformationStateString(Player player) const {
  return HistoryString();
}

std::string OthelloState::ObservationString(Player player) const {
  SPIEL_CHECK_GE(player, 0);
  SPIEL_CHECK_LT(player, num_players_);
  return ToString();
}

void OthelloState::ObservationTensor(Player player,
                                       std::vector<double>* values) const {
  SPIEL_CHECK_GE(player, 0);
  SPIEL_CHECK_LT(player, num_players_);

  // Treat `values` as a 2-d tensor.
  TensorView<2> view(values, {kCellStates, kNumCells}, true);
  for (int cell = 0; cell < kNumCells; ++cell) {
    view[{static_cast<int>(board_[cell]), cell}] = 1.0;
  }
}

void OthelloState::UndoAction(Player player, Action move) {
  SpielFatalError("Undo not implemented for this game.");
}

std::unique_ptr<State> OthelloState::Clone() const {
  return std::unique_ptr<State>(new OthelloState(*this));
}

OthelloGame::OthelloGame(const GameParameters& params)
    : Game(kGameType, params) {}

}  // namespace othello
}  // namespace open_spiel
<commit_msg>removed commented code<commit_after>// Copyright 2019 DeepMind Technologies Ltd. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "open_spiel/games/othello.h"

#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
#include <tuple>

#include "open_spiel/spiel_utils.h"
#include "open_spiel/utils/tensor_view.h"

namespace open_spiel {
namespace othello {
namespace {

// Facts about the game.
const GameType kGameType{
    /*short_name=*/"othello",
    /*long_name=*/"Othello",
    GameType::Dynamics::kSequential,
    GameType::ChanceMode::kDeterministic,
    GameType::Information::kPerfectInformation,
    GameType::Utility::kZeroSum,
    GameType::RewardModel::kTerminal,
    /*max_num_players=*/2,
    /*min_num_players=*/2,
    /*provides_information_state_string=*/true,
    /*provides_information_state_tensor=*/false,
    /*provides_observation_string=*/true,
    /*provides_observation_tensor=*/true,
    /*parameter_specification=*/{}  // no parameters
};

std::shared_ptr<const Game> Factory(const GameParameters& params) {
  return std::shared_ptr<const Game>(new OthelloGame(params));
}

REGISTER_SPIEL_GAME(kGameType, Factory);

}  // namespace

std::tuple<int, int> GetNext(int row, int col, Direction dir) {
  switch (dir) {
    case Direction::kUp:
      return std::make_tuple(row - 1, col);
    case Direction::kDown:
      return std::make_tuple(row + 1, col);
    case Direction::kLeft:
      return std::make_tuple(row, col - 1);
    case Direction::kRight:
      return std::make_tuple(row, col + 1);
    case Direction::kUpRight:
      return std::make_tuple(row - 1, col + 1);
    case Direction::kUpLeft:
      return std::make_tuple(row - 1, col - 1);
    case Direction::kDownRight:
      return std::make_tuple(row + 1, col + 1);
    case Direction::kDownLeft:
      return std::make_tuple(row + 1, col - 1);
    default:
      SpielFatalError(absl::StrCat("Found unmatched case in GetNext."));
  }
}

inline bool OthelloState::OnBoard(int row, int col) const {
  return (0 <= row) && (row < kNumRows) && (0 <= col) && (col < kNumCols);
}

int OthelloState::CountSteps(Player player, int row, int col, Direction dir) const {
  std::tuple<int, int> rowcol = GetNext(row, col, dir);
  row = std::get<0>(rowcol);
  col = std::get<1>(rowcol);

  int count = 0;
  CellState cell = PlayerToState(player);
  while (OnBoard(row, col)) {
    if (BoardAt(row, col) == cell) {
      return count;
    } else if (BoardAt(row, col) == CellState::kEmpty) {
      return 0;
    }

    count++;
    rowcol = GetNext(row, col, dir);
    row = std::get<0>(rowcol);
    col = std::get<1>(rowcol);
  }

  return 0;
}

bool OthelloState::CanCapture(Player player, int move) const {
  if (board_[move] != CellState::kEmpty) return false;

  std::tuple<int, int> row_col = XYFromCode(move);
  int row = std::get<0>(row_col);
  int col = std::get<1>(row_col);

  bool valid = false;
  for (int direction = Direction::kUp; direction < Direction::kLast; direction++) {
    if (CountSteps(player, row, col, static_cast<Direction>(direction)) != 0) {
      return true;
    }
  }

  return false;
}

void OthelloState::Capture(Player player, int row, int col, Direction dir, int steps) {
  std::tuple<int, int> rowcol = GetNext(row, col, dir);
  row = std::get<0>(rowcol);
  col = std::get<1>(rowcol);
  CellState cell = PlayerToState(player);

  for (int step = 0; step < steps; step++) {
    if (BoardAt(row, col) == CellState::kEmpty || BoardAt(row, col) == cell) {
      SpielFatalError(absl::StrCat("Cannot capture cell (", row, ", ", col, ")"));
    }

    board_[row * kNumCols + col] = cell;

    rowcol = GetNext(row, col, dir);
    row = std::get<0>(rowcol);
    col = std::get<1>(rowcol);
  }
}

int OthelloState::DiskCount(Player player) const {
  int count = 0;
  CellState cell = PlayerToState(player);
  for (int i = 0; i < kNumCells; i++) {
    if (board_[i] == cell) count++;
  }

  return count;
}

bool OthelloState::NoValidActions() const {
  return (LegalRegularActions(Player(0)).empty() & LegalRegularActions(Player(1)).empty());
}

std::tuple<int, int> OthelloState::XYFromCode(int move) const {
  if (move >= kNumCells || move < 0) {
    SpielFatalError(absl::StrCat("Move too large: ", move));
  }

  return std::make_tuple(move / kNumCols, move % kNumRows);
}

CellState PlayerToState(Player player) {
  switch (player) {
    case 0:
      return CellState::kBlack;
    case 1:
      return CellState::kWhite;
    default:
      SpielFatalError(absl::StrCat("Invalid player id ", player));
      return CellState::kEmpty;
  }
}

std::string StateToString(CellState state) {
  switch (state) {
    case CellState::kEmpty:
      return ".";
    case CellState::kWhite:
      return "o";
    case CellState::kBlack:
      return "x";
    default:
      SpielFatalError("Unknown state.");
  }
}

bool OthelloState::ValidAction(Player player, int move) const {
  return (board_[move] == CellState::kEmpty && CanCapture(player, move));
}

void OthelloState::DoApplyAction(Action move) {
  if (move == passMove) {  // pass
    current_player_ = 1 - current_player_;
    num_moves_ += 1;
    return;
  }

  if (!ValidAction(current_player_, move)) {
    SpielFatalError(absl::StrCat("Invalid move ", move));
  }

  std::tuple<int, int> rowcol = XYFromCode(move);
  CellState cell = PlayerToState(current_player_);

  int row = std::get<0>(rowcol);
  int col = std::get<1>(rowcol);
  board_[row * kNumCols + col] = cell;

  for (int direction = Direction::kUp; direction < Direction::kLast; direction++) {
    int steps = 0;
    if ((steps = CountSteps(current_player_, row, col, static_cast<Direction>(direction))) > 0) {
      Capture(current_player_, row, col, static_cast<Direction>(direction), steps);
    }
  }

  if (NoValidActions()) {
    int count_zero = DiskCount(Player(0));
    int count_one = DiskCount(Player(1));

    if (count_zero > count_one) {
      outcome_ = Player(0);
    } else if (count_zero < count_one) {
      outcome_ = Player(1);
    }
  }

  current_player_ = 1 - current_player_;
  num_moves_ += 1;
}

std::vector<Action> OthelloState::LegalRegularActions(Player p) const {  // list
  std::vector<Action> moves;
  for (int cell = 0; cell < kNumCells; ++cell) {
    if (ValidAction(p, cell)) {
      moves.push_back(cell);
    }
  }

  return moves;
}

std::vector<Action> OthelloState::LegalActions() const {
  if (IsTerminal()) return {};

  // can move in an empty cell that captures
  std::vector<Action> moves = LegalRegularActions(current_player_);
  if (moves.empty()) {
    moves.push_back(passMove);  // pass
  }

  return moves;
}

std::string OthelloState::ActionToString(Player player,
                                           Action action_id) const {

  if (action_id == passMove) {
    return absl::StrCat(StateToString(PlayerToState(player)), "(pass)");
  } else {
    return absl::StrCat(StateToString(PlayerToState(player)), "(",
                      action_id / kNumCols, ",", action_id % kNumCols, ")");
  }
}

bool OthelloState::IsFull() const {
  for (int i = 0; i < kNumCells; i++) {
    if (board_[i] == CellState::kEmpty) return false;
  }

  return true;
}

OthelloState::OthelloState(std::shared_ptr<const Game> game) : State(game) {
  std::fill(begin(board_), end(board_), CellState::kEmpty);
  board_[27] = CellState::kWhite;
  board_[28] = CellState::kBlack;
  board_[35] = CellState::kBlack;
  board_[36] = CellState::kWhite;
}

std::string OthelloState::ToString() const {
  std::string str;
  for (int r = 0; r < kNumRows; ++r) {
    for (int c = 0; c < kNumCols; ++c) {
      absl::StrAppend(&str, StateToString(BoardAt(r, c)));
    }
    if (r < (kNumRows - 1)) {
      absl::StrAppend(&str, "\n");
    }
  }
  return str;
}

bool OthelloState::IsTerminal() const {
  return outcome_ != kInvalidPlayer || NoValidActions();
}

std::vector<double> OthelloState::Returns() const {
  if (outcome_ == Player{0}) {
    return {1.0, -1.0};
  } else if (outcome_ == Player{1}) {
    return {-1.0, 1.0};
  } else {
    return {0.0, 0.0};
  }
}

std::string OthelloState::InformationStateString(Player player) const {
  return HistoryString();
}

std::string OthelloState::ObservationString(Player player) const {
  SPIEL_CHECK_GE(player, 0);
  SPIEL_CHECK_LT(player, num_players_);
  return ToString();
}

void OthelloState::ObservationTensor(Player player,
                                       std::vector<double>* values) const {
  SPIEL_CHECK_GE(player, 0);
  SPIEL_CHECK_LT(player, num_players_);

  // Treat `values` as a 2-d tensor.
  TensorView<2> view(values, {kCellStates, kNumCells}, true);
  for (int cell = 0; cell < kNumCells; ++cell) {
    view[{static_cast<int>(board_[cell]), cell}] = 1.0;
  }
}

void OthelloState::UndoAction(Player player, Action move) {
  SpielFatalError("Undo not implemented for this game.");
}

std::unique_ptr<State> OthelloState::Clone() const {
  return std::unique_ptr<State>(new OthelloState(*this));
}

OthelloGame::OthelloGame(const GameParameters& params)
    : Game(kGameType, params) {}

}  // namespace othello
}  // namespace open_spiel
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Open Connectome Project (http://openconnecto.me)
 * Written by Da Zheng (zhengda1936@gmail.com)
 *
 * This file is part of FlashMatrix.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mapply_matrix_store.h"
#include "local_matrix_store.h"

namespace fm
{

namespace detail
{

namespace
{

class lmapply_col_matrix_store: public lvirtual_col_matrix_store
{
	std::vector<local_matrix_store::const_ptr> ins;
	const portion_mapply_op &op;
	raw_data_array data;

	void materialize() const {
		local_buf_col_matrix_store res(get_global_start_row(),
				get_global_start_col(), get_num_rows(), get_num_cols(),
				get_type(), -1);
		op.run(ins, res);
		lmapply_col_matrix_store *mutable_this
			= const_cast<lmapply_col_matrix_store *>(this);
		mutable_this->data = res.get_data();
	}
public:
	lmapply_col_matrix_store(
			const std::vector<local_matrix_store::const_ptr> &ins,
			const portion_mapply_op &_op,
			off_t global_start_row, off_t global_start_col,
			size_t nrow, size_t ncol, const scalar_type &type,
			int node_id): lvirtual_col_matrix_store(global_start_row,
				global_start_col, nrow, ncol, type, node_id), op(_op) {
		this->ins = ins;
	}

	virtual const char *get_raw_arr() const {
		if (data.is_empty())
			materialize();
		return data.get_raw();
	}

	virtual matrix_store::const_ptr transpose() const {
		// TODO
		assert(0);
		return matrix_store::const_ptr();
	}

	virtual const char *get_col(size_t col) const {
		if (data.is_empty())
			materialize();
		return data.get_raw() + get_num_rows() * col * get_type().get_size();
	}
};

class lmapply_row_matrix_store: public lvirtual_row_matrix_store
{
	std::vector<local_matrix_store::const_ptr> ins;
	const portion_mapply_op &op;
	raw_data_array data;

	void materialize() const {
		local_buf_row_matrix_store res(get_global_start_row(),
				get_global_start_col(), get_num_rows(), get_num_cols(),
				get_type(), -1);
		op.run(ins, res);
		lmapply_row_matrix_store *mutable_this
			= const_cast<lmapply_row_matrix_store *>(this);
		mutable_this->data = res.get_data();
	}
public:
	lmapply_row_matrix_store(
			const std::vector<local_matrix_store::const_ptr> &ins,
			const portion_mapply_op &_op,
			off_t global_start_row, off_t global_start_col,
			size_t nrow, size_t ncol, const scalar_type &type,
			int node_id): lvirtual_row_matrix_store(global_start_row,
				global_start_col, nrow, ncol, type, node_id), op(_op) {
		this->ins = ins;
	}

	virtual const char *get_raw_arr() const {
		if (data.is_empty())
			materialize();
		return data.get_raw();
	}

	virtual matrix_store::const_ptr transpose() const {
		// TODO
		assert(0);
		return matrix_store::const_ptr();
	}

	virtual const char *get_row(size_t row) const {
		if (data.is_empty())
			materialize();
		return data.get_raw() + get_num_cols() * row * get_type().get_size();
	}

	virtual const char *get_rows(size_t row_start, size_t row_end) const {
		// TODO
		assert(0);
		return NULL;
	}
};

}

mapply_matrix_store::mapply_matrix_store(
		const std::vector<mem_dense_matrix::const_ptr> &in_mats,
		portion_mapply_op::const_ptr op, matrix_layout_t layout,
		size_t nrow, size_t ncol): virtual_matrix_store(nrow, ncol,
			op->get_output_type())
{
	this->layout = layout;
	this->in_mats = in_mats;
	this->op = op;
}

matrix_store::ptr mapply_matrix_store::materialize() const
{
	return _mapply_portion(in_mats, op, layout);
}

matrix_store::const_ptr mapply_matrix_store::get_cols(
		const std::vector<off_t> &idxs) const
{
	// TODO
	assert(0);
	return matrix_store::const_ptr();
}

matrix_store::const_ptr mapply_matrix_store::get_rows(
		const std::vector<off_t> &idxs) const
{
	// TODO
	assert(0);
	return matrix_store::const_ptr();
}

local_matrix_store::const_ptr mapply_matrix_store::get_portion(
			size_t start_row, size_t start_col, size_t num_rows,
			size_t num_cols) const
{
	std::vector<local_matrix_store::const_ptr> parts(in_mats.size());
	if (is_wide()) {
		assert(start_row == 0);
		assert(num_rows == get_num_rows());
		for (size_t i = 0; i < in_mats.size(); i++)
			parts[i] = static_cast<const mem_matrix_store &>(
					in_mats[i]->get_data()).get_portion(start_row, start_col,
					in_mats[i]->get_num_rows(), num_cols);
	}
	else {
		assert(start_col == 0);
		assert(num_cols == get_num_cols());
		for (size_t i = 0; i < in_mats.size(); i++)
			parts[i] = static_cast<const mem_matrix_store &>(
					in_mats[i]->get_data()).get_portion(start_row, start_col,
					num_rows, in_mats[i]->get_num_cols());
	}

	if (store_layout() == matrix_layout_t::L_ROW)
		return local_matrix_store::const_ptr(new lmapply_row_matrix_store(
					parts, *op, start_row, start_col, num_rows, num_cols,
					get_type(), parts.front()->get_node_id()));
	else
		return local_matrix_store::const_ptr(new lmapply_col_matrix_store(
					parts, *op, start_row, start_col, num_rows, num_cols,
					get_type(), parts.front()->get_node_id()));
}

local_matrix_store::const_ptr mapply_matrix_store::get_portion(
			size_t id) const
{
	size_t start_row;
	size_t start_col;
	size_t num_rows;
	size_t num_cols;
	std::pair<size_t, size_t> chunk_size = get_portion_size();
	if (is_wide()) {
		start_row = 0;
		start_col = chunk_size.second * id;
		num_rows = get_num_rows();
		num_cols = std::min(chunk_size.second, get_num_cols() - start_col);
	}
	else {
		start_row = chunk_size.first * id;
		start_col = 0;
		num_rows = std::min(chunk_size.first, get_num_rows() - start_row);
		num_cols = get_num_cols();
	}
	return get_portion(start_row, start_col, num_rows, num_cols);
}

matrix_store::const_ptr mapply_matrix_store::transpose() const
{
	// TODO
	assert(0);
	return matrix_store::const_ptr();
}

}

}
<commit_msg>[Matrix]: optimize mapply_matrix_store for CPU cache.<commit_after>/*
 * Copyright 2015 Open Connectome Project (http://openconnecto.me)
 * Written by Da Zheng (zhengda1936@gmail.com)
 *
 * This file is part of FlashMatrix.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mapply_matrix_store.h"
#include "local_matrix_store.h"

namespace fm
{

namespace detail
{

namespace
{

size_t SUB_CHUNK_SIZE = 4 * 1024;

class mapply_store
{
	std::vector<local_matrix_store::const_ptr> ins;
	const portion_mapply_op &op;
	raw_data_array buf;
	bool materialized;
	local_matrix_store *lstore;
public:
	mapply_store(const std::vector<local_matrix_store::const_ptr> &ins,
			const portion_mapply_op &_op, local_matrix_store *lstore): op(_op) {
		this->lstore = lstore;
		this->ins = ins;
		materialized = false;
	}

	const char *get_raw_arr() const {
		return buf.get_raw();
	}

	bool is_materialized() const {
		return materialized;
	}
	void materialize() const;
	void materialize_whole();

	void resize(off_t local_start_row, off_t local_start_col,
			size_t local_num_rows, size_t local_num_cols);
	void reset_size();
};

void mapply_store::reset_size()
{
	// When the matrix store is resized, we should drop the materialized data.
	materialized = false;
	buf = raw_data_array();
	for (size_t i = 0; i < ins.size(); i++)
		const_cast<local_matrix_store *>(ins[i].get())->reset_size();
}

void mapply_store::resize(off_t local_start_row, off_t local_start_col,
		size_t local_num_rows, size_t local_num_cols)
{
	// When the matrix store is resized, we should drop the materialized data.
	materialized = false;
	// To avoid unnecessary memory allocation, if the new exposed part isn't
	// larger than the buffer size, we can keep the buffer.
	if (local_num_rows * local_num_cols * lstore->get_entry_size()
			> buf.get_num_bytes())
		buf = raw_data_array();

	if (lstore->is_wide()) {
		assert(local_start_row == 0 && local_num_rows == lstore->get_num_rows());
		for (size_t i = 0; i < ins.size(); i++)
			const_cast<local_matrix_store *>(ins[i].get())->resize(0,
					local_start_col, ins[i]->get_num_rows(), local_num_cols);
	}
	else {
		assert(local_start_col == 0 && local_num_cols == lstore->get_num_cols());
		for (size_t i = 0; i < ins.size(); i++)
			const_cast<local_matrix_store *>(ins[i].get())->resize(
					local_start_row, 0, local_num_rows, ins[i]->get_num_cols());
	}
}

void mapply_store::materialize_whole()
{
	local_matrix_store::ptr res;
	if (lstore->store_layout() == matrix_layout_t::L_COL) {
		local_buf_col_matrix_store *tmp = new local_buf_col_matrix_store(
				lstore->get_global_start_row(),
				lstore->get_global_start_col(), lstore->get_num_rows(),
				lstore->get_num_cols(), lstore->get_type(), -1);
		res = local_matrix_store::ptr(tmp);
		this->buf = tmp->get_data();
	}
	else {
		local_buf_row_matrix_store *tmp = new local_buf_row_matrix_store(
				lstore->get_global_start_row(),
				lstore->get_global_start_col(), lstore->get_num_rows(),
				lstore->get_num_cols(), lstore->get_type(), -1);
		res = local_matrix_store::ptr(tmp);
		this->buf = tmp->get_data();
	}

	std::vector<local_matrix_store *> mutable_ins(ins.size());
	for (size_t i = 0; i < ins.size(); i++)
		mutable_ins[i] = const_cast<local_matrix_store *>(ins[i].get());

	if (lstore->is_wide()) {
		for (size_t local_start_col = 0; local_start_col < lstore->get_num_cols();
				local_start_col += SUB_CHUNK_SIZE) {
			size_t local_num_cols = std::min(SUB_CHUNK_SIZE,
					lstore->get_num_cols() - local_start_col);
			for (size_t i = 0; i < mutable_ins.size(); i++) {
				mutable_ins[i]->resize(0, local_start_col,
						mutable_ins[i]->get_num_rows(), local_num_cols);
			}
			res->resize(0, local_start_col, res->get_num_rows(), local_num_cols);
			op.run(ins, *res);
		}
	}
	else {
		// If this is a tall matrix.
		for (size_t local_start_row = 0; local_start_row < lstore->get_num_rows();
				local_start_row += SUB_CHUNK_SIZE) {
			size_t local_num_rows = std::min(SUB_CHUNK_SIZE,
					lstore->get_num_rows() - local_start_row);
			for (size_t i = 0; i < mutable_ins.size(); i++) {
				mutable_ins[i]->resize(local_start_row, 0, local_num_rows,
						mutable_ins[i]->get_num_cols());
			}
			res->resize(local_start_row, 0, local_num_rows, res->get_num_cols());
			op.run(ins, *res);
		}
	}
	for (size_t i = 0; i < mutable_ins.size(); i++)
		mutable_ins[i]->reset_size();
}

void mapply_store::materialize() const
{
	mapply_store *mutable_this = const_cast<mapply_store *>(this);
	if (lstore->is_whole())
		mutable_this->materialize_whole();
	else if (lstore->store_layout() == matrix_layout_t::L_COL
			&& this->buf.is_empty()) {
		local_buf_col_matrix_store res(lstore->get_global_start_row(),
				lstore->get_global_start_col(), lstore->get_num_rows(),
				lstore->get_num_cols(), lstore->get_type(), -1);
		op.run(ins, res);
		mutable_this->buf = res.get_data();
	}
	else if (lstore->store_layout() == matrix_layout_t::L_COL) {
		local_ref_contig_col_matrix_store res(mutable_this->buf.get_raw(),
				lstore->get_global_start_row(), lstore->get_global_start_col(),
				lstore->get_num_rows(), lstore->get_num_cols(),
				lstore->get_type(), -1);
		op.run(ins, res);
	}
	else if (this->buf.is_empty()) {
		local_buf_row_matrix_store res(lstore->get_global_start_row(),
				lstore->get_global_start_col(), lstore->get_num_rows(),
				lstore->get_num_cols(), lstore->get_type(), -1);
		op.run(ins, res);
		mutable_this->buf = res.get_data();
	}
	else {
		local_ref_contig_row_matrix_store res(mutable_this->buf.get_raw(),
				lstore->get_global_start_row(), lstore->get_global_start_col(),
				lstore->get_num_rows(), lstore->get_num_cols(),
				lstore->get_type(), -1);
		op.run(ins, res);
	}
	mutable_this->materialized = true;
}

class lmapply_col_matrix_store: public lvirtual_col_matrix_store
{
	mapply_store store;
public:
	lmapply_col_matrix_store(
			const std::vector<local_matrix_store::const_ptr> &ins,
			const portion_mapply_op &op,
			off_t global_start_row, off_t global_start_col,
			size_t nrow, size_t ncol, const scalar_type &type,
			int node_id): lvirtual_col_matrix_store(global_start_row,
				global_start_col, nrow, ncol, type, node_id),
			store(ins, op, this) {
	}

	virtual bool resize(off_t local_start_row, off_t local_start_col,
			size_t local_num_rows, size_t local_num_cols) {
		store.resize(local_start_row, local_start_col, local_num_rows,
				local_num_cols);
		return local_matrix_store::resize(local_start_row, local_start_col,
				local_num_rows, local_num_cols);
	}
	virtual void reset_size() {
		store.reset_size();
		local_matrix_store::reset_size();
	}

	virtual const char *get_raw_arr() const {
		if (!store.is_materialized())
			store.materialize();
		return store.get_raw_arr();
	}

	virtual matrix_store::const_ptr transpose() const {
		// TODO
		assert(0);
		return matrix_store::const_ptr();
	}

	virtual const char *get_col(size_t col) const {
		if (!store.is_materialized())
			store.materialize();
		return store.get_raw_arr() + get_num_rows() * col * get_type().get_size();
	}
};

class lmapply_row_matrix_store: public lvirtual_row_matrix_store
{
	mapply_store store;
public:
	lmapply_row_matrix_store(
			const std::vector<local_matrix_store::const_ptr> &ins,
			const portion_mapply_op &op,
			off_t global_start_row, off_t global_start_col,
			size_t nrow, size_t ncol, const scalar_type &type,
			int node_id): lvirtual_row_matrix_store(global_start_row,
				global_start_col, nrow, ncol, type, node_id),
			store(ins, op, this) {
	}

	virtual bool resize(off_t local_start_row, off_t local_start_col,
			size_t local_num_rows, size_t local_num_cols) {
		store.resize(local_start_row, local_start_col, local_num_rows,
				local_num_cols);
		return local_matrix_store::resize(local_start_row, local_start_col,
				local_num_rows, local_num_cols);
	}
	virtual void reset_size() {
		store.reset_size();
		local_matrix_store::reset_size();
	}

	virtual const char *get_raw_arr() const {
		if (!store.is_materialized())
			store.materialize();
		return store.get_raw_arr();
	}

	virtual matrix_store::const_ptr transpose() const {
		// TODO
		assert(0);
		return matrix_store::const_ptr();
	}

	virtual const char *get_row(size_t row) const {
		if (!store.is_materialized())
			store.materialize();
		return store.get_raw_arr() + get_num_cols() * row * get_type().get_size();
	}

	virtual const char *get_rows(size_t row_start, size_t row_end) const {
		// TODO
		assert(0);
		return NULL;
	}
};

}

mapply_matrix_store::mapply_matrix_store(
		const std::vector<mem_dense_matrix::const_ptr> &in_mats,
		portion_mapply_op::const_ptr op, matrix_layout_t layout,
		size_t nrow, size_t ncol): virtual_matrix_store(nrow, ncol,
			op->get_output_type())
{
	this->layout = layout;
	this->in_mats = in_mats;
	this->op = op;
}

matrix_store::ptr mapply_matrix_store::materialize() const
{
	return _mapply_portion(in_mats, op, layout);
}

matrix_store::const_ptr mapply_matrix_store::get_cols(
		const std::vector<off_t> &idxs) const
{
	// TODO
	assert(0);
	return matrix_store::const_ptr();
}

matrix_store::const_ptr mapply_matrix_store::get_rows(
		const std::vector<off_t> &idxs) const
{
	// TODO
	assert(0);
	return matrix_store::const_ptr();
}

local_matrix_store::const_ptr mapply_matrix_store::get_portion(
			size_t start_row, size_t start_col, size_t num_rows,
			size_t num_cols) const
{
	std::vector<local_matrix_store::const_ptr> parts(in_mats.size());
	if (is_wide()) {
		assert(start_row == 0);
		assert(num_rows == get_num_rows());
		for (size_t i = 0; i < in_mats.size(); i++)
			parts[i] = static_cast<const mem_matrix_store &>(
					in_mats[i]->get_data()).get_portion(start_row, start_col,
					in_mats[i]->get_num_rows(), num_cols);
	}
	else {
		assert(start_col == 0);
		assert(num_cols == get_num_cols());
		for (size_t i = 0; i < in_mats.size(); i++)
			parts[i] = static_cast<const mem_matrix_store &>(
					in_mats[i]->get_data()).get_portion(start_row, start_col,
					num_rows, in_mats[i]->get_num_cols());
	}

	if (store_layout() == matrix_layout_t::L_ROW)
		return local_matrix_store::const_ptr(new lmapply_row_matrix_store(
					parts, *op, start_row, start_col, num_rows, num_cols,
					get_type(), parts.front()->get_node_id()));
	else
		return local_matrix_store::const_ptr(new lmapply_col_matrix_store(
					parts, *op, start_row, start_col, num_rows, num_cols,
					get_type(), parts.front()->get_node_id()));
}

local_matrix_store::const_ptr mapply_matrix_store::get_portion(
			size_t id) const
{
	size_t start_row;
	size_t start_col;
	size_t num_rows;
	size_t num_cols;
	std::pair<size_t, size_t> chunk_size = get_portion_size();
	if (is_wide()) {
		start_row = 0;
		start_col = chunk_size.second * id;
		num_rows = get_num_rows();
		num_cols = std::min(chunk_size.second, get_num_cols() - start_col);
	}
	else {
		start_row = chunk_size.first * id;
		start_col = 0;
		num_rows = std::min(chunk_size.first, get_num_rows() - start_row);
		num_cols = get_num_cols();
	}
	return get_portion(start_row, start_col, num_rows, num_cols);
}

matrix_store::const_ptr mapply_matrix_store::transpose() const
{
	// TODO
	assert(0);
	return matrix_store::const_ptr();
}

}

}
<|endoftext|>


<commit_before>#pragma once
#include <memory>
#include <iostream>
#include "error.hpp"
#include <boost/serialization/access.hpp>
#include <boost/serialization/vector.hpp>

// clangでshared_ptrが定義されない問題への一時的な対処
#include <boost/serialization/singleton.hpp>
#include <boost/serialization/extended_type_info.hpp>
#undef BOOST_NO_CXX11_SMART_PTR
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/weak_ptr.hpp>

namespace spn {
	DEF_HASMETHOD(clone)
	// cloneメソッドを持っていればそれを呼ぶ
	template <class T>
	auto CopyAsSP(T& t, std::true_type) {
		return t.clone();
	}
	// cloneメソッドを持っていない時はコピーコンストラクタで対処
	template <class T>
	auto CopyAsSP(T& t, std::false_type) {
		using Tc = typename std::remove_const<T>::type;
		return std::shared_ptr<Tc>(new Tc(t));
	}
	//! iterateDepthFirstの戻り値
	enum class Iterate {
		ReturnFromChild,	//!< 内部用
		StepIn,				//!< 子を巡回
		Next,				//!< 子を巡回せず兄弟ノードへ進む
		Quit				//!< 直ちに巡回を終える
	};
	//! 汎用ツリー構造
	/*! Tは必ずTreeNode<T>を継承する前提 */
	template <class T>
	class TreeNode : public std::enable_shared_from_this<T> {
		private:
			friend class boost::serialization::access;
			template <class Ar>
			void serialize(Ar& ar, const unsigned int) {
				ar	& BOOST_SERIALIZATION_NVP(_spChild)
					& BOOST_SERIALIZATION_NVP(_wpParent)
					& BOOST_SERIALIZATION_NVP(_spSibling);
			}

		// 巡回参照を避けるために親ノードはweak_ptrで格納
		public:
			using this_t = TreeNode<T>;
			using pointer = this_t*;
			using SP = std::shared_ptr<T>;
			using SPC = std::shared_ptr<const T>;
			using WP = std::weak_ptr<T>;
			using SPVector = std::vector<SP>;
			using SPCVector = std::vector<SPC>;

			SP			_spChild,
						_spSibling;
			WP			_wpParent;

			// --- ノードつなぎ変え時に呼ばれる関数. 継承先クラスが適時オーバーライドする ---
			void onParentChange(const SP& /*from*/, const SP& /*to*/) {}
			void onChildRemove(const SP& /*node*/) {}
			void onChildAdded(const SP& /*node*/) {}

			static void _PrintIndent(std::ostream& os, int n) {
				while(--n >= 0)
					os << '\t';
			}
			template <class CB>
			void _iterateAll(CB&& cb) const {
				auto* self = const_cast<this_t*>(this);
				self->template iterateDepthFirst<true>([&cb](auto& nd, int){
					cb(nd);
					return Iterate::StepIn;
				});
			}

			template <class T_SP>
			std::vector<T_SP> _plain() const {
				std::vector<T_SP> spv;
				_iterateAll([&spv](auto& nd){
					spv.emplace_back(nd.shared_from_this());
				});
				return spv;
			}
			template <class T_PTR>
			std::vector<T_PTR> _plainPtr() const {
				std::vector<T_PTR> pv;
				_iterateAll([&pv](auto& nd){
					pv.emplace_back(&nd);
				});
				return pv;
			}

		public:
			TreeNode() = default;
			explicit TreeNode(TreeNode&& t) = default;
			//! copy-ctorに置いてはリンク情報をコピーしない
			explicit TreeNode(const TreeNode&) {}
			// コピー禁止
			TreeNode& operator = (const TreeNode&) = delete;
			// ムーブは可
			TreeNode& operator = (TreeNode&& t) = default;

			void setParent(const SP& s) {
				setParent(WP(s));
			}
			void setParent(const WP& w) {
				SP sp = w.lock(),
				   spP = getParent();
				AssertP(Trap, sp.get() != this, "self-reference detected")
				if(sp.get() != spP.get()) {
					onParentChange(spP, sp);
					_wpParent = w;
				}
			}

			SP getParent() const {
				return SP(_wpParent.lock());
			}
			const SP& getChild() const {
				return _spChild;
			}
			const SP& getSibling() const {
				return _spSibling;
			}
			SP getPrevSibling(const bool bLoop=false) const {
				const SP p = getParent();
				if(p) {
					auto cur = p->getChild();
					if(!cur->getSibling()) {
						// 項目が1つしかない
						return nullptr;
					}
					// カーソルがリストの最初
					if(cur.get() == this) {
						if(bLoop) {
							// カーソルを末尾にループ
							while(const auto& tmp = cur->getSibling())
								cur = tmp;
							return cur;
						}
						return nullptr;
					}
					// 直前の項目を探す
					while(const auto& cur2 = cur->getSibling()) {
						if(cur2.get() == this)
							return cur;
						cur = cur2;
					}
				}
				return nullptr;
			}
			void removeChild(const SP& target) {
				AssertP(Trap, _spChild)
				if(_spChild == target) {
					onChildRemove(target);
					// 最初の子ノードが削除対象
					target->setParent(WP());
					_spChild = target->getSibling();
					target->_spSibling = nullptr;
				} else {
					// 兄弟ノードのどれかが削除対象
					_spChild->removeSibling(nullptr, target);
				}
			}
			void removeSibling(pointer prev, const SP& target) {
				if(target.get() == this) {
					if(auto sp = getParent())
						sp->onChildRemove(this->shared_from_this());
					// このノードが削除対象
					AssertP(Trap, prev)
					prev->_spSibling = _spSibling;
					setParent(WP());
					_spSibling = nullptr;
					return;
				}
				AssertP(Trap, _spSibling)
				_spSibling->removeSibling(this, target);
			}
			void addChild(const SP& s) {
				AssertP(Trap, s.get() != this, "self-reference detected")
				if(_spChild)
					_spChild->addSibling(s);
				else {
					_spChild = s;
					s->setParent(this->shared_from_this());
					onChildAdded(s);
				}
			}
			void addSibling(const SP& s) {
				AssertP(Trap, s.get() != this, "self-reference detected")
				if(_spSibling)
					_spSibling->addSibling(s);
				else {
					_spSibling = s;
					s->setParent(_wpParent);
					if(auto sp = getParent())
						sp->onChildAdded(s);
				}
			}
			//! 深さ優先で巡回
			/*! \tparam Sib		兄弟ノードを巡回対象に加えるか
				\tparam BConst	trueならconst巡回 */
			template <bool Sib, class Callback, bool BConst=false>
			Iterate iterateDepthFirst(Callback&& cb, int depth=0) {
				using thistc = std::conditional_t<BConst, const T, T>;
				Iterate t = cb(static_cast<thistc&>(*this), depth);
				if(t == Iterate::Quit)
					return Iterate::Quit;
				if(t == Iterate::StepIn) {
					if(_spChild) {
						if(_spChild->template iterateDepthFirst<true>(std::forward<Callback>(cb), depth+1) == Iterate::Quit)
							return Iterate::Quit;
					}
				}
				if(Sib && _spSibling)
					return _spSibling->template iterateDepthFirst<true>(std::forward<Callback>(cb), depth);
				return Iterate::ReturnFromChild;
			}
			template <bool Sib, class Callback>
			Iterate iterateDepthFirst(Callback&& cb, int depth=0) const {
				return const_cast<this_t*>(this)->template iterateDepthFirst<Sib, Callback, true>(std::forward<Callback>(cb), depth);
			}
			template <class Callback>
			SP find(Callback&& cb) const {
				SP ret;
				iterateDepthFirst<false>([&cb, &ret](auto& nd, int){
					if(cb(nd)) {
						ret = nd.shared_from_this();
						return Iterate::Quit;
					}
					return Iterate::StepIn;
				});
				return ret;
			}
			//! このノード以下を全て複製
			SP cloneTree(const WP& parent=WP()) const {
				SP sp = CopyAsSP(static_cast<const T&>(*this),
								HasMethod_clone<T>(nullptr));
				if(_spChild)
					sp->_spChild = _spChild->cloneTree(sp);
				if(_spSibling)
					sp->_spSibling = _spSibling->cloneTree(parent);
				sp->_wpParent = parent;
				return sp;
			}
			//! 主にデバッグ用
			void print(std::ostream& os, int indent) const {
				_PrintIndent(os, indent);
				// 明示的なダウンキャスト
				os << *static_cast<const T*>(this);
			}
			//! ツリー構造を配列化
			SPVector plain() {
				return _plain<SP>();
			}
			//! ツリー構造を配列化 (const)
			SPCVector plain() const {
				return _plain<SPC>();
			}
			//! ツリー構造を配列化 (pointer)
			std::vector<T*> plainPtr() {
				return _plainPtr<T*>();
			}
			//! ツリー構造を配列化 (const pointer)
			std::vector<const T*> plainPtr() const {
				return _plainPtr<const T*>();
			}
			//! 最大ツリー深度を取得
			int getDepth() const {
				int depth = 0;
				if(_spChild)
					depth = _spChild->getDepth() + 1;
				if(_spSibling)
					depth = std::max(depth, _spSibling->getDepth());
				return depth;
			}
	};
	template <class T0, class T1, class CMP>
	bool _CompareTree(const TreeNode<T0>& t0, const TreeNode<T1>& t1, CMP&& cmp) {
		auto fnParentIndex = [](const auto& ar, const auto& p){
			if(!p)
				return ar.end() - ar.begin();
			auto itr = std::find_if(ar.begin(), ar.end(), [&p](auto& r){ return r == p.get(); });
			return itr - ar.begin();
		};
		// 配列化して親ノード番号をチェック
		auto ar0 = t0.plainPtr();
		auto ar1 = t1.plainPtr();
		if(ar0.size() != ar1.size())
			return false;
		auto sz = ar0.size();
		for(size_t i=0 ; i<sz ; i++) {
			// 親ノード番号
			int idx0 = fnParentIndex(ar0, ar0[i]->getParent()),
				idx1 = fnParentIndex(ar1, ar1[i]->getParent());
			if(idx0!=idx1 || !cmp(ar0[i], ar1[i]))
				return false;
		}
		return true;
	}
	// ツリー構造のみを比較する
	template <class T0, class T1>
	bool CompareTreeStructure(const TreeNode<T0>& t0, const TreeNode<T1>& t1) {
		return _CompareTree(t0, t1, [](auto&,auto&){ return true; });
	}
	//! データも含めて比較
	template <class T0, class T1>
	bool CompareTree(const TreeNode<T0>& t0, const TreeNode<T1>& t1) {
		auto cmp = [](auto& sp0, auto& sp1){
			return *sp0 == *sp1;
		};
		return _CompareTree(t0, t1, cmp);
	}
	template <class T>
	inline std::ostream& operator << (std::ostream& os, const TreeNode<T>& t) {
		auto& self = const_cast<TreeNode<T>&>(t);
		self.iterateDepthFirst<false>([&os](auto& s, int indent){
			s.print(os, indent);
			os << std::endl;
			return TreeNode<T>::Iterate::StepIn;
		});
		return os;
	}
}

<commit_msg>TreeNode: 子ノードのソート関数<commit_after>#pragma once
#include <memory>
#include <iostream>
#include "error.hpp"
#include <boost/serialization/access.hpp>
#include <boost/serialization/vector.hpp>

// clangでshared_ptrが定義されない問題への一時的な対処
#include <boost/serialization/singleton.hpp>
#include <boost/serialization/extended_type_info.hpp>
#undef BOOST_NO_CXX11_SMART_PTR
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/weak_ptr.hpp>

namespace spn {
	DEF_HASMETHOD(clone)
	// cloneメソッドを持っていればそれを呼ぶ
	template <class T>
	auto CopyAsSP(T& t, std::true_type) {
		return t.clone();
	}
	// cloneメソッドを持っていない時はコピーコンストラクタで対処
	template <class T>
	auto CopyAsSP(T& t, std::false_type) {
		using Tc = typename std::remove_const<T>::type;
		return std::shared_ptr<Tc>(new Tc(t));
	}
	//! iterateDepthFirstの戻り値
	enum class Iterate {
		ReturnFromChild,	//!< 内部用
		StepIn,				//!< 子を巡回
		Next,				//!< 子を巡回せず兄弟ノードへ進む
		Quit				//!< 直ちに巡回を終える
	};
	//! 汎用ツリー構造
	/*! Tは必ずTreeNode<T>を継承する前提 */
	template <class T>
	class TreeNode : public std::enable_shared_from_this<T> {
		private:
			friend class boost::serialization::access;
			template <class Ar>
			void serialize(Ar& ar, const unsigned int) {
				ar	& BOOST_SERIALIZATION_NVP(_spChild)
					& BOOST_SERIALIZATION_NVP(_wpParent)
					& BOOST_SERIALIZATION_NVP(_spSibling);
			}

		// 巡回参照を避けるために親ノードはweak_ptrで格納
		public:
			using Iterate = ::spn::Iterate;
			using this_t = TreeNode<T>;
			using pointer = this_t*;
			using element_type = T;
			using SP = std::shared_ptr<T>;
			using SPC = std::shared_ptr<const T>;
			using WP = std::weak_ptr<T>;
			using SPVector = std::vector<SP>;
			using SPCVector = std::vector<SPC>;

			SP			_spChild,
						_spSibling;
			WP			_wpParent;

			// --- ノードつなぎ変え時に呼ばれる関数. 継承先クラスが適時オーバーライドする ---
			void onParentChange(const SP& /*from*/, const SP& /*to*/) {}
			void onChildRemove(const SP& /*node*/) {}
			void onChildAdded(const SP& /*node*/) {}

			static void _PrintIndent(std::ostream& os, int n) {
				while(--n >= 0)
					os << '\t';
			}
			template <class CB>
			void _iterateAll(CB&& cb) const {
				auto* self = const_cast<this_t*>(this);
				self->template iterateDepthFirst<true>([&cb](auto& nd, int){
					cb(nd);
					return Iterate::StepIn;
				});
			}

			template <class T_SP>
			std::vector<T_SP> _plain() const {
				std::vector<T_SP> spv;
				_iterateAll([&spv](auto& nd){
					spv.emplace_back(nd.shared_from_this());
				});
				return spv;
			}
			template <class T_PTR>
			std::vector<T_PTR> _plainPtr() const {
				std::vector<T_PTR> pv;
				_iterateAll([&pv](auto& nd){
					pv.emplace_back(&nd);
				});
				return pv;
			}
			template <class CB>
			void _iterateChild(CB&& cb) const {
				if(SP sp = getChild()) {
					do {
						cb(sp);
						sp = sp->getSibling();
					} while(sp);
				}
			}

		public:
			TreeNode() = default;
			explicit TreeNode(TreeNode&& t) = default;
			//! copy-ctorに置いてはリンク情報をコピーしない
			explicit TreeNode(const TreeNode&) {}
			// コピー禁止
			TreeNode& operator = (const TreeNode&) = delete;
			// ムーブは可
			TreeNode& operator = (TreeNode&& t) = default;

			//! 任意の基準で子ノードをソート
			template <class CMP>
			void sortChild(CMP&& cmp, const bool recursive) {
				if(auto c = getChild()) {
					SPVector spv;
					_iterateChild([&spv](auto& nd){
						spv.emplace_back(nd);
					});
					std::sort(spv.begin(), spv.end(), cmp);
					SP	cur = spv.front(),
						pcur;
					_spChild = cur;
					auto itr = spv.begin();
					do {
						cur = *itr;
						if(pcur)
							pcur->_spSibling = cur;
						pcur = cur;
					} while(++itr != spv.end());
					cur->_spSibling.reset();

					if(recursive) {
						for(auto& s : spv)
							s->sortChild(cmp, recursive);
					}
				}
			}
			void setParent(const SP& s) {
				setParent(WP(s));
			}
			void setParent(const WP& w) {
				SP sp = w.lock(),
				   spP = getParent();
				AssertP(Trap, sp.get() != this, "self-reference detected")
				if(sp.get() != spP.get()) {
					onParentChange(spP, sp);
					_wpParent = w;
				}
			}

			SP getParent() const {
				return SP(_wpParent.lock());
			}
			const SP& getChild() const {
				return _spChild;
			}
			const SP& getSibling() const {
				return _spSibling;
			}
			SP getPrevSibling(const bool bLoop=false) const {
				const SP p = getParent();
				if(p) {
					auto cur = p->getChild();
					if(!cur->getSibling()) {
						// 項目が1つしかない
						return nullptr;
					}
					// カーソルがリストの最初
					if(cur.get() == this) {
						if(bLoop) {
							// カーソルを末尾にループ
							while(const auto& tmp = cur->getSibling())
								cur = tmp;
							return cur;
						}
						return nullptr;
					}
					// 直前の項目を探す
					while(const auto& cur2 = cur->getSibling()) {
						if(cur2.get() == this)
							return cur;
						cur = cur2;
					}
				}
				return nullptr;
			}
			void removeChild(const SP& target) {
				AssertP(Trap, _spChild)
				if(_spChild == target) {
					onChildRemove(target);
					// 最初の子ノードが削除対象
					target->setParent(WP());
					_spChild = target->getSibling();
					target->_spSibling = nullptr;
				} else {
					// 兄弟ノードのどれかが削除対象
					_spChild->removeSibling(nullptr, target);
				}
			}
			void removeSibling(pointer prev, const SP& target) {
				if(target.get() == this) {
					if(auto sp = getParent())
						sp->onChildRemove(this->shared_from_this());
					// このノードが削除対象
					AssertP(Trap, prev)
					prev->_spSibling = _spSibling;
					setParent(WP());
					_spSibling = nullptr;
					return;
				}
				AssertP(Trap, _spSibling)
				_spSibling->removeSibling(this, target);
			}
			void addChild(const SP& s) {
				AssertP(Trap, s.get() != this, "self-reference detected")
				if(_spChild)
					_spChild->addSibling(s);
				else {
					_spChild = s;
					s->setParent(this->shared_from_this());
					onChildAdded(s);
				}
			}
			void addSibling(const SP& s) {
				AssertP(Trap, s.get() != this, "self-reference detected")
				if(_spSibling)
					_spSibling->addSibling(s);
				else {
					_spSibling = s;
					s->setParent(_wpParent);
					if(auto sp = getParent())
						sp->onChildAdded(s);
				}
			}
			//! 深さ優先で巡回
			/*! \tparam Sib		兄弟ノードを巡回対象に加えるか
				\tparam BConst	trueならconst巡回 */
			template <bool Sib, class Callback, bool BConst=false>
			Iterate iterateDepthFirst(Callback&& cb, int depth=0) {
				using thistc = std::conditional_t<BConst, const T, T>;
				Iterate t = cb(static_cast<thistc&>(*this), depth);
				if(t == Iterate::Quit)
					return Iterate::Quit;
				if(t == Iterate::StepIn) {
					if(_spChild) {
						if(_spChild->template iterateDepthFirst<true>(std::forward<Callback>(cb), depth+1) == Iterate::Quit)
							return Iterate::Quit;
					}
				}
				if(Sib && _spSibling)
					return _spSibling->template iterateDepthFirst<true>(std::forward<Callback>(cb), depth);
				return Iterate::ReturnFromChild;
			}
			template <bool Sib, class Callback>
			Iterate iterateDepthFirst(Callback&& cb, int depth=0) const {
				return const_cast<this_t*>(this)->template iterateDepthFirst<Sib, Callback, true>(std::forward<Callback>(cb), depth);
			}
			template <class Callback>
			SP find(Callback&& cb) const {
				SP ret;
				iterateDepthFirst<false>([&cb, &ret](auto& nd, int){
					if(cb(nd)) {
						ret = nd.shared_from_this();
						return Iterate::Quit;
					}
					return Iterate::StepIn;
				});
				return ret;
			}
			//! このノード以下を全て複製
			SP cloneTree(const WP& parent=WP()) const {
				SP sp = CopyAsSP(static_cast<const T&>(*this),
								HasMethod_clone<T>(nullptr));
				if(_spChild)
					sp->_spChild = _spChild->cloneTree(sp);
				if(_spSibling)
					sp->_spSibling = _spSibling->cloneTree(parent);
				sp->_wpParent = parent;
				return sp;
			}
			//! 主にデバッグ用
			void print(std::ostream& os, int indent) const {
				_PrintIndent(os, indent);
				// 明示的なダウンキャスト
				os << *static_cast<const T*>(this);
			}
			//! ツリー構造を配列化
			SPVector plain() {
				return _plain<SP>();
			}
			//! ツリー構造を配列化 (const)
			SPCVector plain() const {
				return _plain<SPC>();
			}
			//! ツリー構造を配列化 (pointer)
			std::vector<T*> plainPtr() {
				return _plainPtr<T*>();
			}
			//! ツリー構造を配列化 (const pointer)
			std::vector<const T*> plainPtr() const {
				return _plainPtr<const T*>();
			}
			//! 最大ツリー深度を取得
			int getDepth() const {
				int depth = 0;
				if(_spChild)
					depth = _spChild->getDepth() + 1;
				if(_spSibling)
					depth = std::max(depth, _spSibling->getDepth());
				return depth;
			}
	};
	template <class T0, class T1, class CMP>
	bool _CompareTree(const TreeNode<T0>& t0, const TreeNode<T1>& t1, CMP&& cmp) {
		auto fnParentIndex = [](const auto& ar, const auto& p){
			if(!p)
				return ar.end() - ar.begin();
			auto itr = std::find_if(ar.begin(), ar.end(), [&p](auto& r){ return r == p.get(); });
			return itr - ar.begin();
		};
		// 配列化して親ノード番号をチェック
		auto ar0 = t0.plainPtr();
		auto ar1 = t1.plainPtr();
		if(ar0.size() != ar1.size())
			return false;
		auto sz = ar0.size();
		for(size_t i=0 ; i<sz ; i++) {
			// 親ノード番号
			int idx0 = fnParentIndex(ar0, ar0[i]->getParent()),
				idx1 = fnParentIndex(ar1, ar1[i]->getParent());
			if(idx0!=idx1 || !cmp(ar0[i], ar1[i]))
				return false;
		}
		return true;
	}
	// ツリー構造のみを比較する
	template <class T0, class T1>
	bool CompareTreeStructure(const TreeNode<T0>& t0, const TreeNode<T1>& t1) {
		return _CompareTree(t0, t1, [](auto&,auto&){ return true; });
	}
	//! データも含めて比較
	template <class T0, class T1>
	bool CompareTree(const TreeNode<T0>& t0, const TreeNode<T1>& t1) {
		auto cmp = [](auto& sp0, auto& sp1){
			return *sp0 == *sp1;
		};
		return _CompareTree(t0, t1, cmp);
	}
	template <class T>
	inline std::ostream& operator << (std::ostream& os, const TreeNode<T>& t) {
		auto& self = const_cast<TreeNode<T>&>(t);
		self.iterateDepthFirst<false>([&os](auto& s, int indent){
			s.print(os, indent);
			os << std::endl;
			return TreeNode<T>::Iterate::StepIn;
		});
		return os;
	}
}

<|endoftext|>


<commit_before>#include "../stdafx.h"
#include "list_view.h"

namespace uih {

void ListView::destroy_header()
{
    if (m_wnd_header) {
        DestroyWindow(m_wnd_header);
        m_wnd_header = nullptr;
        m_header_font.reset();
    }
}

void ListView::create_header()
{
    // if (m_show_header)
    {
        if (!m_wnd_header) {
            m_header_font.reset(m_lf_header_valid ? CreateFontIndirect(&m_lf_header) : uih::create_icon_font());
            m_wnd_header = CreateWindowEx(0, WC_HEADER, _T("NGLVH"),
                WS_CHILD | (0) | /*(m_autosize ? 0x0800  : NULL) |*/ HDS_HOTTRACK
                    | (m_allow_header_rearrange ? HDS_DRAGDROP : NULL) | HDS_HORZ | HDS_FULLDRAG
                    | (m_sorting_enabled ? HDS_BUTTONS : 0) | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
                0, 0, 0, 0, get_wnd(), HMENU(IDC_HEADER), mmh::get_current_instance(), nullptr);
            SetWindowTheme(m_wnd_header, m_use_dark_mode ? L"DarkMode_ItemsView" : L"ItemsView", nullptr);
            SendMessage(m_wnd_header, WM_SETFONT, (WPARAM)m_header_font.get(), MAKELPARAM(FALSE, 0));
            if (m_initialised) {
                build_header();
                ShowWindow(m_wnd_header, SW_SHOWNORMAL);
            }
        }
    }
}

void ListView::set_show_header(bool b_val)
{
    if (b_val != m_show_header) {
        m_show_header = b_val;
        if (m_initialised) {
            if (m_show_header)
                create_header();
            else
                destroy_header();
            on_size();
        }
    }
}

void ListView::reposition_header()
{
    RECT rc;
    GetClientRect(get_wnd(), &rc);
    int cx = RECT_CX(rc);
    RECT rc_header;
    get_header_rect(&rc_header);
    SetWindowPos(m_wnd_header, nullptr, -m_horizontal_scroll_position, 0, cx + m_horizontal_scroll_position,
        RECT_CY(rc_header), SWP_NOZORDER);
}

void ListView::build_header()
{
    if (m_wnd_header) {
        pfc::vartoggle_t<bool> toggle(m_ignore_column_size_change_notification, true);

        t_size header_count = Header_GetItemCount(m_wnd_header);
        for (; header_count; header_count--)
            Header_DeleteItem(m_wnd_header, header_count - 1);

        HDITEM hdi;
        memset(&hdi, 0, sizeof(HDITEM));

        hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
        hdi.fmt = HDF_LEFT | HDF_STRING;

        pfc::string8 name;
        pfc::stringcvt::string_wide_from_utf8 wstr;

        {
            int n;
            int t = m_columns.size();
            int i = 0;
            const auto indentation = get_total_indentation();
            if (indentation /*m_group_count*/) {
                hdi.fmt = HDF_STRING | HDF_LEFT;
                hdi.cchTextMax = 0;
                hdi.pszText = const_cast<wchar_t*>(L"");

                hdi.cxy = indentation;

                Header_InsertItem(m_wnd_header, i++, &hdi);

                m_have_indent_column = true;
            } else
                m_have_indent_column = false;
            for (n = 0; n < t; n++) {
                hdi.fmt = HDF_STRING | HDF_LEFT;
                if (m_columns[n].m_alignment == uih::ALIGN_CENTRE)
                    hdi.fmt |= HDF_CENTER;
                else if (m_columns[n].m_alignment == uih::ALIGN_RIGHT)
                    hdi.fmt |= HDF_RIGHT;
                hdi.cchTextMax = m_columns[n].m_title.length();
                wstr.convert(m_columns[n].m_title);
                hdi.pszText = const_cast<wchar_t*>(wstr.get_ptr());

                hdi.cxy = m_columns[n].m_display_size;

                if (m_sort_column_index == n && m_show_sort_indicators) {
                    hdi.fmt |= (m_sort_direction ? HDF_SORTDOWN : HDF_SORTUP);
                }

                Header_InsertItem(m_wnd_header, i++, &hdi);
            }
        }
    }
}

bool ListView::on_wm_notify_header(LPNMHDR lpnm, LRESULT& ret)
{
    switch (lpnm->code) {
    case NM_CUSTOMDRAW: {
        LPNMCUSTOMDRAW lpcd = (LPNMCUSTOMDRAW)lpnm;
        switch (lpcd->dwDrawStage) {
        case CDDS_PREPAINT:
            ret = CDRF_NOTIFYITEMDRAW;
            return true;

        case CDDS_ITEMPREPAINT: {
            auto cr{RGB(255, 0, 0)};
            if (!SUCCEEDED(GetThemeColor(m_header_theme.get(), HP_HEADERITEM, 0, TMT_TEXTCOLOR, &cr)))
                return false;

            SetTextColor(lpcd->hdc, cr);
            ret = CDRF_NEWFONT;
            return true;
        }
        default:
            return false;
        }
    }
    case HDN_BEGINTRACKA:
    case HDN_BEGINTRACKW: {
        auto lpnmh = (LPNMHEADER)lpnm;
        if (m_autosize && (!get_show_group_info_area() || lpnmh->iItem)) {
            ret = TRUE;
            return true;
        }
    } break;
    case HDN_DIVIDERDBLCLICK: {
        auto lpnmh = (LPNMHEADER)lpnm;
        if (!m_autosize) {
            if (lpnmh->iItem != -1 && (!m_have_indent_column || lpnmh->iItem)) {
                t_size realIndex = lpnmh->iItem;
                if (m_have_indent_column)
                    realIndex--;
                if (realIndex < m_columns.size()) {
                    HDC dc;
                    dc = GetDC(get_wnd());
                    int size;

                    HFONT fnt_old = SelectFont(dc, m_items_font.get());

                    int w = 0;
                    int n;
                    int t = get_item_count();

                    for (n = 0; n < t; n++) {
                        const char* str = get_item_text(n, realIndex);
                        size = uih::get_text_width_colour(dc, str, strlen(str));
                        if (size > w)
                            w = size;
                    }
                    w += uih::scale_dpi_value(3) * 2 + scale_dpi_value(1);

                    SelectFont(dc, fnt_old);
                    ReleaseDC(get_wnd(), dc);

                    m_columns[realIndex].m_size = w;
                    m_columns[realIndex].m_display_size = w;
                    update_header();
                    invalidate_all();
                    notify_on_column_size_change(realIndex, m_columns[realIndex].m_size);
                    update_scroll_info();
                }
            }
        }
    } break;
    case HDN_ITEMCHANGING: {
        if (!m_ignore_column_size_change_notification) {
            auto lpnmh = (LPNMHEADER)lpnm;
            if (lpnmh->pitem->mask & HDI_WIDTH) {
                if (m_have_indent_column && lpnmh->iItem == 0) {
                    int min_indent = get_item_indentation();
                    if (get_show_group_info_area())
                        min_indent += get_default_indentation_step();
                    if (lpnmh->pitem->cxy < min_indent) {
                        ret = TRUE;
                        return true;
                    }
                }
            }
        }
    } break;
    case HDN_ITEMCHANGED: {
        if (!m_ignore_column_size_change_notification) {
            auto lpnmh = (LPNMHEADER)lpnm;
            if (lpnmh->pitem->mask & HDI_WIDTH) {
                if (lpnmh->iItem != -1) {
                    if (m_have_indent_column && lpnmh->iItem == 0) {
                        int new_size = lpnmh->pitem->cxy - get_item_indentation() - get_default_indentation_step();
                        if (new_size >= 0 && new_size != get_group_info_area_width()) {
                            /*set_group_info_area_size(new_size);
                            if (m_autosize)
                            {
                                update_column_sizes();
                                update_header();
                            }*/
                            notify_on_group_info_area_size_change(new_size);
                            /*invalidate_all();
                            update_scroll_info();*/
                        }
                    } else if (!m_autosize) {
                        t_size realIndex = lpnmh->iItem;
                        if (m_have_indent_column)
                            realIndex--;
                        if (realIndex < m_columns.size() && m_columns[realIndex].m_display_size != lpnmh->pitem->cxy) {
                            m_columns[realIndex].m_size = lpnmh->pitem->cxy;
                            m_columns[realIndex].m_display_size = lpnmh->pitem->cxy;
                            invalidate_all();
                            notify_on_column_size_change(realIndex, m_columns[realIndex].m_size);
                            on_size();
                        }
                    }
                }
            }
        }
    } break;
    case HDN_ITEMCLICK: {
        auto lpnmh = (LPNMHEADER)lpnm;
        if (lpnmh->iItem != -1 && (!m_have_indent_column || lpnmh->iItem)) {
            t_size realIndex = lpnmh->iItem;
            if (m_have_indent_column)
                realIndex--;
            if (realIndex < m_columns.size()) {
                bool des = (realIndex == m_sort_column_index ? !m_sort_direction : false);
                sort_by_column(realIndex, des);
            }
        }
    } break;
    /*case HDN_BEGINDRAG:
        {
            LPNMHEADER lpnmh = (LPNMHEADER)lpnm;
            if (lpnmh->iItem == 0 && m_have_indent_column)
            {
                ret = TRUE;
                return true;
            }
        }
        break;*/
    case HDN_ENDDRAG: {
        auto lpnmh = (LPNMHEADER)lpnm;
        if (lpnmh->iButton == 0) {
            if (lpnmh->pitem && (lpnmh->pitem->mask & HDI_ORDER)) {
                int from = lpnmh->iItem;
                int to = lpnmh->pitem->iOrder;
                if (m_have_indent_column) {
                    from--;
                    to--;
                }
                if (to >= 0 && from >= 0 && from != to) {
                    notify_on_header_rearrange(from, to);
                }
            }
        }
    }
        ret = TRUE;
        return true;
    }
    return false;
}

void ListView::get_header_rect(LPRECT rc) const
{
    if (m_wnd_header)
        *rc = uih::get_relative_rect(m_wnd_header, get_wnd());
    else {
        GetClientRect(get_wnd(), rc);
        rc->bottom = rc->top;
    }
}

unsigned ListView::get_header_height() const
{
    unsigned ret = 0;
    if (m_wnd_header) {
        RECT rc;
        get_header_rect(&rc);
        ret = RECT_CY(rc);
    }
    return ret;
}
unsigned ListView::calculate_header_height()
{
    unsigned rv = 0;
    if (m_wnd_header) {
        auto font = (HFONT)SendMessage(m_wnd_header, WM_GETFONT, 0, 0);
        rv = uih::get_font_height(font) + m_vertical_item_padding + scale_dpi_value(2);
    }
    return rv;
}

void ListView::update_header()
{
    if (m_wnd_header) {
        pfc::vartoggle_t<bool> toggle(m_ignore_column_size_change_notification, true);
        // SendMessage(m_wnd_header, WM_SETREDRAW, FALSE, NULL);
        t_size i;
        t_size count = m_columns.size();
        t_size j = 0;
        if (m_have_indent_column) {
            uih::header_set_item_width(m_wnd_header, j, get_total_indentation());
            j++;
        }
        for (i = 0; i < count; i++) {
            uih::header_set_item_width(m_wnd_header, i + j, m_columns[i].m_display_size);
        }
        // SendMessage(m_wnd_header, WM_SETREDRAW, TRUE, NULL);
        // RedrawWindow(m_wnd_header, NULL, NULL, RDW_INVALIDATE|(b_update?RDW_UPDATENOW:0));
    }
}
} // namespace uih
<commit_msg>Don't set list view header window theme in non-dark mode<commit_after>#include "../stdafx.h"
#include "list_view.h"

namespace uih {

void ListView::destroy_header()
{
    if (m_wnd_header) {
        DestroyWindow(m_wnd_header);
        m_wnd_header = nullptr;
        m_header_font.reset();
    }
}

void ListView::create_header()
{
    // if (m_show_header)
    {
        if (!m_wnd_header) {
            m_header_font.reset(m_lf_header_valid ? CreateFontIndirect(&m_lf_header) : uih::create_icon_font());
            m_wnd_header = CreateWindowEx(0, WC_HEADER, _T("NGLVH"),
                WS_CHILD | (0) | /*(m_autosize ? 0x0800  : NULL) |*/ HDS_HOTTRACK
                    | (m_allow_header_rearrange ? HDS_DRAGDROP : NULL) | HDS_HORZ | HDS_FULLDRAG
                    | (m_sorting_enabled ? HDS_BUTTONS : 0) | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
                0, 0, 0, 0, get_wnd(), HMENU(IDC_HEADER), mmh::get_current_instance(), nullptr);
            SetWindowTheme(m_wnd_header, m_use_dark_mode ? L"DarkMode_ItemsView" : nullptr, nullptr);
            SendMessage(m_wnd_header, WM_SETFONT, (WPARAM)m_header_font.get(), MAKELPARAM(FALSE, 0));
            if (m_initialised) {
                build_header();
                ShowWindow(m_wnd_header, SW_SHOWNORMAL);
            }
        }
    }
}

void ListView::set_show_header(bool b_val)
{
    if (b_val != m_show_header) {
        m_show_header = b_val;
        if (m_initialised) {
            if (m_show_header)
                create_header();
            else
                destroy_header();
            on_size();
        }
    }
}

void ListView::reposition_header()
{
    RECT rc;
    GetClientRect(get_wnd(), &rc);
    int cx = RECT_CX(rc);
    RECT rc_header;
    get_header_rect(&rc_header);
    SetWindowPos(m_wnd_header, nullptr, -m_horizontal_scroll_position, 0, cx + m_horizontal_scroll_position,
        RECT_CY(rc_header), SWP_NOZORDER);
}

void ListView::build_header()
{
    if (m_wnd_header) {
        pfc::vartoggle_t<bool> toggle(m_ignore_column_size_change_notification, true);

        t_size header_count = Header_GetItemCount(m_wnd_header);
        for (; header_count; header_count--)
            Header_DeleteItem(m_wnd_header, header_count - 1);

        HDITEM hdi;
        memset(&hdi, 0, sizeof(HDITEM));

        hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
        hdi.fmt = HDF_LEFT | HDF_STRING;

        pfc::string8 name;
        pfc::stringcvt::string_wide_from_utf8 wstr;

        {
            int n;
            int t = m_columns.size();
            int i = 0;
            const auto indentation = get_total_indentation();
            if (indentation /*m_group_count*/) {
                hdi.fmt = HDF_STRING | HDF_LEFT;
                hdi.cchTextMax = 0;
                hdi.pszText = const_cast<wchar_t*>(L"");

                hdi.cxy = indentation;

                Header_InsertItem(m_wnd_header, i++, &hdi);

                m_have_indent_column = true;
            } else
                m_have_indent_column = false;
            for (n = 0; n < t; n++) {
                hdi.fmt = HDF_STRING | HDF_LEFT;
                if (m_columns[n].m_alignment == uih::ALIGN_CENTRE)
                    hdi.fmt |= HDF_CENTER;
                else if (m_columns[n].m_alignment == uih::ALIGN_RIGHT)
                    hdi.fmt |= HDF_RIGHT;
                hdi.cchTextMax = m_columns[n].m_title.length();
                wstr.convert(m_columns[n].m_title);
                hdi.pszText = const_cast<wchar_t*>(wstr.get_ptr());

                hdi.cxy = m_columns[n].m_display_size;

                if (m_sort_column_index == n && m_show_sort_indicators) {
                    hdi.fmt |= (m_sort_direction ? HDF_SORTDOWN : HDF_SORTUP);
                }

                Header_InsertItem(m_wnd_header, i++, &hdi);
            }
        }
    }
}

bool ListView::on_wm_notify_header(LPNMHDR lpnm, LRESULT& ret)
{
    switch (lpnm->code) {
    case NM_CUSTOMDRAW: {
        LPNMCUSTOMDRAW lpcd = (LPNMCUSTOMDRAW)lpnm;
        switch (lpcd->dwDrawStage) {
        case CDDS_PREPAINT:
            ret = CDRF_NOTIFYITEMDRAW;
            return true;

        case CDDS_ITEMPREPAINT: {
            auto cr{RGB(255, 0, 0)};
            if (!SUCCEEDED(GetThemeColor(m_header_theme.get(), HP_HEADERITEM, 0, TMT_TEXTCOLOR, &cr)))
                return false;

            SetTextColor(lpcd->hdc, cr);
            ret = CDRF_NEWFONT;
            return true;
        }
        default:
            return false;
        }
    }
    case HDN_BEGINTRACKA:
    case HDN_BEGINTRACKW: {
        auto lpnmh = (LPNMHEADER)lpnm;
        if (m_autosize && (!get_show_group_info_area() || lpnmh->iItem)) {
            ret = TRUE;
            return true;
        }
    } break;
    case HDN_DIVIDERDBLCLICK: {
        auto lpnmh = (LPNMHEADER)lpnm;
        if (!m_autosize) {
            if (lpnmh->iItem != -1 && (!m_have_indent_column || lpnmh->iItem)) {
                t_size realIndex = lpnmh->iItem;
                if (m_have_indent_column)
                    realIndex--;
                if (realIndex < m_columns.size()) {
                    HDC dc;
                    dc = GetDC(get_wnd());
                    int size;

                    HFONT fnt_old = SelectFont(dc, m_items_font.get());

                    int w = 0;
                    int n;
                    int t = get_item_count();

                    for (n = 0; n < t; n++) {
                        const char* str = get_item_text(n, realIndex);
                        size = uih::get_text_width_colour(dc, str, strlen(str));
                        if (size > w)
                            w = size;
                    }
                    w += uih::scale_dpi_value(3) * 2 + scale_dpi_value(1);

                    SelectFont(dc, fnt_old);
                    ReleaseDC(get_wnd(), dc);

                    m_columns[realIndex].m_size = w;
                    m_columns[realIndex].m_display_size = w;
                    update_header();
                    invalidate_all();
                    notify_on_column_size_change(realIndex, m_columns[realIndex].m_size);
                    update_scroll_info();
                }
            }
        }
    } break;
    case HDN_ITEMCHANGING: {
        if (!m_ignore_column_size_change_notification) {
            auto lpnmh = (LPNMHEADER)lpnm;
            if (lpnmh->pitem->mask & HDI_WIDTH) {
                if (m_have_indent_column && lpnmh->iItem == 0) {
                    int min_indent = get_item_indentation();
                    if (get_show_group_info_area())
                        min_indent += get_default_indentation_step();
                    if (lpnmh->pitem->cxy < min_indent) {
                        ret = TRUE;
                        return true;
                    }
                }
            }
        }
    } break;
    case HDN_ITEMCHANGED: {
        if (!m_ignore_column_size_change_notification) {
            auto lpnmh = (LPNMHEADER)lpnm;
            if (lpnmh->pitem->mask & HDI_WIDTH) {
                if (lpnmh->iItem != -1) {
                    if (m_have_indent_column && lpnmh->iItem == 0) {
                        int new_size = lpnmh->pitem->cxy - get_item_indentation() - get_default_indentation_step();
                        if (new_size >= 0 && new_size != get_group_info_area_width()) {
                            /*set_group_info_area_size(new_size);
                            if (m_autosize)
                            {
                                update_column_sizes();
                                update_header();
                            }*/
                            notify_on_group_info_area_size_change(new_size);
                            /*invalidate_all();
                            update_scroll_info();*/
                        }
                    } else if (!m_autosize) {
                        t_size realIndex = lpnmh->iItem;
                        if (m_have_indent_column)
                            realIndex--;
                        if (realIndex < m_columns.size() && m_columns[realIndex].m_display_size != lpnmh->pitem->cxy) {
                            m_columns[realIndex].m_size = lpnmh->pitem->cxy;
                            m_columns[realIndex].m_display_size = lpnmh->pitem->cxy;
                            invalidate_all();
                            notify_on_column_size_change(realIndex, m_columns[realIndex].m_size);
                            on_size();
                        }
                    }
                }
            }
        }
    } break;
    case HDN_ITEMCLICK: {
        auto lpnmh = (LPNMHEADER)lpnm;
        if (lpnmh->iItem != -1 && (!m_have_indent_column || lpnmh->iItem)) {
            t_size realIndex = lpnmh->iItem;
            if (m_have_indent_column)
                realIndex--;
            if (realIndex < m_columns.size()) {
                bool des = (realIndex == m_sort_column_index ? !m_sort_direction : false);
                sort_by_column(realIndex, des);
            }
        }
    } break;
    /*case HDN_BEGINDRAG:
        {
            LPNMHEADER lpnmh = (LPNMHEADER)lpnm;
            if (lpnmh->iItem == 0 && m_have_indent_column)
            {
                ret = TRUE;
                return true;
            }
        }
        break;*/
    case HDN_ENDDRAG: {
        auto lpnmh = (LPNMHEADER)lpnm;
        if (lpnmh->iButton == 0) {
            if (lpnmh->pitem && (lpnmh->pitem->mask & HDI_ORDER)) {
                int from = lpnmh->iItem;
                int to = lpnmh->pitem->iOrder;
                if (m_have_indent_column) {
                    from--;
                    to--;
                }
                if (to >= 0 && from >= 0 && from != to) {
                    notify_on_header_rearrange(from, to);
                }
            }
        }
    }
        ret = TRUE;
        return true;
    }
    return false;
}

void ListView::get_header_rect(LPRECT rc) const
{
    if (m_wnd_header)
        *rc = uih::get_relative_rect(m_wnd_header, get_wnd());
    else {
        GetClientRect(get_wnd(), rc);
        rc->bottom = rc->top;
    }
}

unsigned ListView::get_header_height() const
{
    unsigned ret = 0;
    if (m_wnd_header) {
        RECT rc;
        get_header_rect(&rc);
        ret = RECT_CY(rc);
    }
    return ret;
}
unsigned ListView::calculate_header_height()
{
    unsigned rv = 0;
    if (m_wnd_header) {
        auto font = (HFONT)SendMessage(m_wnd_header, WM_GETFONT, 0, 0);
        rv = uih::get_font_height(font) + m_vertical_item_padding + scale_dpi_value(2);
    }
    return rv;
}

void ListView::update_header()
{
    if (m_wnd_header) {
        pfc::vartoggle_t<bool> toggle(m_ignore_column_size_change_notification, true);
        // SendMessage(m_wnd_header, WM_SETREDRAW, FALSE, NULL);
        t_size i;
        t_size count = m_columns.size();
        t_size j = 0;
        if (m_have_indent_column) {
            uih::header_set_item_width(m_wnd_header, j, get_total_indentation());
            j++;
        }
        for (i = 0; i < count; i++) {
            uih::header_set_item_width(m_wnd_header, i + j, m_columns[i].m_display_size);
        }
        // SendMessage(m_wnd_header, WM_SETREDRAW, TRUE, NULL);
        // RedrawWindow(m_wnd_header, NULL, NULL, RDW_INVALIDATE|(b_update?RDW_UPDATENOW:0));
    }
}
} // namespace uih
<|endoftext|>


<commit_before>#ifndef TEST_HELPER_H_
#define TEST_HELPER_H_

#include <internal/iterbase.hpp>

#include <sstream>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <vector>

namespace itertest {

  // non-copyable. non-movable. non-default-constructible
  class SolidInt {
   private:
    const int i;

   public:
    constexpr SolidInt(int n) : i{n} {}

    constexpr int getint() const {
      return this->i;
    }

    SolidInt() = delete;
    SolidInt(const SolidInt&) = delete;
    SolidInt& operator=(const SolidInt&) = delete;
    SolidInt& operator=(SolidInt&&) = delete;
    SolidInt(SolidInt&&) = delete;
  };

  namespace {
    struct DoubleDereferenceError : std::exception {
      const char* what() const noexcept override {
        return "Iterator dereferenced twice without increment";
      }
    };

    // this class's iterator will throw if it's dereference twice without
    // an increment in between
    class InputIterable {
     public:
      class Iterator {
       private:
        int i;
        bool was_incremented = true;

       public:
        Iterator(int n) : i{n} {}

        Iterator& operator++() {
          ++this->i;
          this->was_incremented = true;
          return *this;
        }

        int operator*() {
          if (!this->was_incremented) {
            throw DoubleDereferenceError{};
          }
          this->was_incremented = false;
          return this->i;
        }

        bool operator!=(const Iterator& other) const {
          return this->i != other.i;
        }
      };

      Iterator begin() {
        return {0};
      }

      Iterator end() {
        return {5};
      }
    };
  }

  // BasicIterable provides a minimal forward iterator
  // operator++(), operator!=(const BasicIterable&), operator*()
  // move constructible only
  // not copy constructible, move assignable, or copy assignable
  template <typename T>
  class BasicIterable {
   private:
    T* data;
    std::size_t size;
    bool was_moved_from_ = false;
    mutable bool was_copied_from_ = false;

   public:
    BasicIterable(std::initializer_list<T> il)
        : data{new T[il.size()]}, size{il.size()} {
      // would like to use enumerate, can't because it's for unit
      // testing enumerate
      std::size_t i = 0;
      for (auto&& e : il) {
        data[i] = e;
        ++i;
      }
    }

    BasicIterable& operator=(BasicIterable&&) = delete;
    BasicIterable& operator=(const BasicIterable&) = delete;

#ifndef DEFINE_BASIC_ITERABLE_COPY_CTOR
    BasicIterable(const BasicIterable&) = delete;
#else
    BasicIterable(const BasicIterable& other)
        : data{new T[other.size]}, size{other.size} {
      other.was_copied_from_ = true;
      auto o_it = begin(other);
      for (auto it = begin(*this); o_it != end(other); ++it, ++o_it) {
        *it = *o_it;
      }
    }
#endif

    BasicIterable(BasicIterable&& other) : data{other.data}, size{other.size} {
      other.data = nullptr;
      other.was_moved_from_ = true;
    }

    bool was_moved_from() const {
      return this->was_moved_from_;
    }

    bool was_copied_from() const {
      return this->was_copied_from_;
    }

    ~BasicIterable() {
      delete[] this->data;
    }

    template <typename U>
    class Iterator {
     private:
      U* p;

     public:
#ifdef DEFINE_DEFAULT_ITERATOR_CTOR
      Iterator() = default;
#endif
      Iterator(U* b) : p{b} {}
      bool operator!=(const Iterator& other) const {
        return this->p != other.p;
      }

      Iterator& operator++() {
        ++this->p;
        return *this;
      }

      U& operator*() {
        return *this->p;
      }
    };

    friend BasicIterable::Iterator<T> begin(BasicIterable& b) {
      return {b.data};
    }

    friend BasicIterable::Iterator<T> end(BasicIterable& b) {
      return {b.data + b.size};
    }

#ifdef DEFINE_BASIC_ITERABLE_CONST_BEGIN_AND_END
    friend BasicIterable::Iterator<const T> begin(const BasicIterable& b) {
      return {b.data};
    }

    friend BasicIterable::Iterator<const T> end(const BasicIterable& b) {
      return {b.data + b.size};
    }
#endif

#ifdef DECLARE_REVERSE_ITERATOR
    Iterator<T> rbegin();
    Iterator<T> rend();
#endif  // ifdef DECLARE_REVERSE_ITERATOR
  };

  template <typename, typename = void>
  struct IsIterator : std::false_type {};

  template <typename T>
  struct IsIterator<T,
      std::void_t<decltype(T(std::declval<const T&>())),            // copyctor
          decltype(std::declval<T&>() = std::declval<const T&>()),  // copy =
          decltype(*std::declval<T&>()),                            // operator*
          decltype(std::declval<T&>().operator->()),  // operator->
          decltype(++std::declval<T&>()),             // prefix ++
          decltype(std::declval<T&>()++),             // postfix ++
          decltype(
              std::declval<const T&>() != std::declval<const T&>()),      //  !=
          decltype(std::declval<const T&>() == std::declval<const T&>())  //  ==
          >> : std::true_type {};

  template <typename T>
  struct IsForwardIterator
      : std::integral_constant<bool,
            IsIterator<T>::value && std::is_default_constructible<T>::value> {};

  template <typename T>
  struct IsMoveConstructibleOnly
      : std::integral_constant<bool,
            !std::is_copy_constructible<T>::value
                && !std::is_copy_assignable<T>::value
                && !std::is_move_assignable<T>::value
                && std::is_move_constructible<T>::value> {};

  struct Point {
    int x;
    int y;
    int get_x() const {
      return x;
    }
    int get_y() const {
      return y;
    }

    std::string prefix(const std::string& str) {
      std::ostringstream ss;
      ss << str << "(" << x << ", " << y << ")";
      return ss.str();
    }
  };
}
template <typename T, typename Inc>
class DiffEndRange {
 private:
  T start_;
  T stop_;
  std::vector<T> all_results_;

 public:
  constexpr DiffEndRange(T start, T stop) : start_{start}, stop_{stop} {
    while (start < stop_) {
      all_results_.push_back(start);
      Inc{}(start);
    }
  }

  class Iterator;
  class EndIterator;

  class Iterator {
    using SubIter = typename std::vector<T>::iterator;

   private:
    SubIter it_;
    SubIter end_;

   public:
#ifdef CHAR_RANGE_DEFAULT_CONSTRUCTIBLE
    Iterator() = default;
#endif
    Iterator(SubIter it, SubIter end_it) : it_{it}, end_{end_it} {}

    T& operator*() const {
      return *it_;
    }
    T* operator->() const {
      return &*it_;
    }

    Iterator& operator++() {
      ++it_;
      return *this;
    }

    bool operator!=(const Iterator& other) const {
      return it_ != other.it_;
    }

    bool operator!=(const EndIterator&) const {
      return it_ != end_;
    }

    friend bool operator!=(const EndIterator& lhs, const Iterator& rhs) {
      return rhs != lhs;
    }
  };

  class ReverseIterator {
    using SubIter = typename std::vector<T>::reverse_iterator;

   private:
    SubIter it_;
    SubIter end_;

   public:
#ifdef CHAR_RANGE_DEFAULT_CONSTRUCTIBLE
    ReverseIterator() = default;
#endif
    ReverseIterator(SubIter it, SubIter end_it) : it_{it}, end_{end_it} {}

    T& operator*() const {
      return *it_;
    }
    T* operator->() const {
      return &*it_;
    }

    Iterator& operator++() {
      ++it_;
      return *this;
    }

    bool operator!=(const Iterator& other) const {
      return it_ != other.it_;
    }

    bool operator!=(const EndIterator&) const {
      return it_ != end_;
    }

    friend bool operator!=(const EndIterator& lhs, const Iterator& rhs) {
      return rhs != lhs;
    }
  };

  class EndIterator {};
  class ReverseEndIterator {};

  Iterator begin() {
    return {std::begin(all_results_), std::end(all_results_)};
  }

  EndIterator end() {
    return {};
  }

  ReverseIterator rbegin() {
    return {std::rbegin(all_results_), std::rend(all_results_)};
  }

  ReverseEndIterator rend() {
    return {};
  }
};

struct CharInc {
  void operator()(char& c) {
    ++c;
  }
};

// A range from 'a' to stop, begin() and end() are different
class CharRange : public DiffEndRange<char, CharInc> {
 public:
  constexpr CharRange(char stop) : DiffEndRange<char, CharInc>('a', stop) {}
};

struct IncIntCharPair {
  void operator()(std::pair<int, char>& p) {
    ++p.first;
    ++p.second;
  }
};

class IntCharPairRange
    : public DiffEndRange<std::pair<int, char>, IncIntCharPair> {
 public:
  IntCharPairRange(std::pair<int, char> stop)
      : DiffEndRange<std::pair<int, char>, IncIntCharPair>({0, 'a'}, stop) {}
};

#endif
<commit_msg>Adds == and != operators to Point<commit_after>#ifndef TEST_HELPER_H_
#define TEST_HELPER_H_

#include <internal/iterbase.hpp>

#include <sstream>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <vector>

namespace itertest {

  // non-copyable. non-movable. non-default-constructible
  class SolidInt {
   private:
    const int i;

   public:
    constexpr SolidInt(int n) : i{n} {}

    constexpr int getint() const {
      return this->i;
    }

    SolidInt() = delete;
    SolidInt(const SolidInt&) = delete;
    SolidInt& operator=(const SolidInt&) = delete;
    SolidInt& operator=(SolidInt&&) = delete;
    SolidInt(SolidInt&&) = delete;
  };

  namespace {
    struct DoubleDereferenceError : std::exception {
      const char* what() const noexcept override {
        return "Iterator dereferenced twice without increment";
      }
    };

    // this class's iterator will throw if it's dereference twice without
    // an increment in between
    class InputIterable {
     public:
      class Iterator {
       private:
        int i;
        bool was_incremented = true;

       public:
        Iterator(int n) : i{n} {}

        Iterator& operator++() {
          ++this->i;
          this->was_incremented = true;
          return *this;
        }

        int operator*() {
          if (!this->was_incremented) {
            throw DoubleDereferenceError{};
          }
          this->was_incremented = false;
          return this->i;
        }

        bool operator!=(const Iterator& other) const {
          return this->i != other.i;
        }
      };

      Iterator begin() {
        return {0};
      }

      Iterator end() {
        return {5};
      }
    };
  }

  // BasicIterable provides a minimal forward iterator
  // operator++(), operator!=(const BasicIterable&), operator*()
  // move constructible only
  // not copy constructible, move assignable, or copy assignable
  template <typename T>
  class BasicIterable {
   private:
    T* data;
    std::size_t size;
    bool was_moved_from_ = false;
    mutable bool was_copied_from_ = false;

   public:
    BasicIterable(std::initializer_list<T> il)
        : data{new T[il.size()]}, size{il.size()} {
      // would like to use enumerate, can't because it's for unit
      // testing enumerate
      std::size_t i = 0;
      for (auto&& e : il) {
        data[i] = e;
        ++i;
      }
    }

    BasicIterable& operator=(BasicIterable&&) = delete;
    BasicIterable& operator=(const BasicIterable&) = delete;

#ifndef DEFINE_BASIC_ITERABLE_COPY_CTOR
    BasicIterable(const BasicIterable&) = delete;
#else
    BasicIterable(const BasicIterable& other)
        : data{new T[other.size]}, size{other.size} {
      other.was_copied_from_ = true;
      auto o_it = begin(other);
      for (auto it = begin(*this); o_it != end(other); ++it, ++o_it) {
        *it = *o_it;
      }
    }
#endif

    BasicIterable(BasicIterable&& other) : data{other.data}, size{other.size} {
      other.data = nullptr;
      other.was_moved_from_ = true;
    }

    bool was_moved_from() const {
      return this->was_moved_from_;
    }

    bool was_copied_from() const {
      return this->was_copied_from_;
    }

    ~BasicIterable() {
      delete[] this->data;
    }

    template <typename U>
    class Iterator {
     private:
      U* p;

     public:
#ifdef DEFINE_DEFAULT_ITERATOR_CTOR
      Iterator() = default;
#endif
      Iterator(U* b) : p{b} {}
      bool operator!=(const Iterator& other) const {
        return this->p != other.p;
      }

      Iterator& operator++() {
        ++this->p;
        return *this;
      }

      U& operator*() {
        return *this->p;
      }
    };

    friend BasicIterable::Iterator<T> begin(BasicIterable& b) {
      return {b.data};
    }

    friend BasicIterable::Iterator<T> end(BasicIterable& b) {
      return {b.data + b.size};
    }

#ifdef DEFINE_BASIC_ITERABLE_CONST_BEGIN_AND_END
    friend BasicIterable::Iterator<const T> begin(const BasicIterable& b) {
      return {b.data};
    }

    friend BasicIterable::Iterator<const T> end(const BasicIterable& b) {
      return {b.data + b.size};
    }
#endif

#ifdef DECLARE_REVERSE_ITERATOR
    Iterator<T> rbegin();
    Iterator<T> rend();
#endif  // ifdef DECLARE_REVERSE_ITERATOR
  };

  template <typename, typename = void>
  struct IsIterator : std::false_type {};

  template <typename T>
  struct IsIterator<T,
      std::void_t<decltype(T(std::declval<const T&>())),            // copyctor
          decltype(std::declval<T&>() = std::declval<const T&>()),  // copy =
          decltype(*std::declval<T&>()),                            // operator*
          decltype(std::declval<T&>().operator->()),  // operator->
          decltype(++std::declval<T&>()),             // prefix ++
          decltype(std::declval<T&>()++),             // postfix ++
          decltype(
              std::declval<const T&>() != std::declval<const T&>()),      //  !=
          decltype(std::declval<const T&>() == std::declval<const T&>())  //  ==
          >> : std::true_type {};

  template <typename T>
  struct IsForwardIterator
      : std::integral_constant<bool,
            IsIterator<T>::value && std::is_default_constructible<T>::value> {};

  template <typename T>
  struct IsMoveConstructibleOnly
      : std::integral_constant<bool,
            !std::is_copy_constructible<T>::value
                && !std::is_copy_assignable<T>::value
                && !std::is_move_assignable<T>::value
                && std::is_move_constructible<T>::value> {};

  struct Point {
    int x;
    int y;
    int get_x() const {
      return x;
    }
    int get_y() const {
      return y;
    }

    std::string prefix(const std::string& str) {
      std::ostringstream ss;
      ss << str << "(" << x << ", " << y << ")";
      return ss.str();
    }

    bool operator==(Point other) const {
      return x == other.x && y == other.y;
    }

    bool operator!=(Point other) const {
      return !(*this == other);
    }
  };
}
template <typename T, typename Inc>
class DiffEndRange {
 private:
  T start_;
  T stop_;
  std::vector<T> all_results_;

 public:
  constexpr DiffEndRange(T start, T stop) : start_{start}, stop_{stop} {
    while (start < stop_) {
      all_results_.push_back(start);
      Inc{}(start);
    }
  }

  class Iterator;
  class EndIterator;

  class Iterator {
    using SubIter = typename std::vector<T>::iterator;

   private:
    SubIter it_;
    SubIter end_;

   public:
#ifdef CHAR_RANGE_DEFAULT_CONSTRUCTIBLE
    Iterator() = default;
#endif
    Iterator(SubIter it, SubIter end_it) : it_{it}, end_{end_it} {}

    T& operator*() const {
      return *it_;
    }
    T* operator->() const {
      return &*it_;
    }

    Iterator& operator++() {
      ++it_;
      return *this;
    }

    bool operator!=(const Iterator& other) const {
      return it_ != other.it_;
    }

    bool operator!=(const EndIterator&) const {
      return it_ != end_;
    }

    friend bool operator!=(const EndIterator& lhs, const Iterator& rhs) {
      return rhs != lhs;
    }
  };

  class ReverseIterator {
    using SubIter = typename std::vector<T>::reverse_iterator;

   private:
    SubIter it_;
    SubIter end_;

   public:
#ifdef CHAR_RANGE_DEFAULT_CONSTRUCTIBLE
    ReverseIterator() = default;
#endif
    ReverseIterator(SubIter it, SubIter end_it) : it_{it}, end_{end_it} {}

    T& operator*() const {
      return *it_;
    }
    T* operator->() const {
      return &*it_;
    }

    Iterator& operator++() {
      ++it_;
      return *this;
    }

    bool operator!=(const Iterator& other) const {
      return it_ != other.it_;
    }

    bool operator!=(const EndIterator&) const {
      return it_ != end_;
    }

    friend bool operator!=(const EndIterator& lhs, const Iterator& rhs) {
      return rhs != lhs;
    }
  };

  class EndIterator {};
  class ReverseEndIterator {};

  Iterator begin() {
    return {std::begin(all_results_), std::end(all_results_)};
  }

  EndIterator end() {
    return {};
  }

  ReverseIterator rbegin() {
    return {std::rbegin(all_results_), std::rend(all_results_)};
  }

  ReverseEndIterator rend() {
    return {};
  }
};

struct CharInc {
  void operator()(char& c) {
    ++c;
  }
};

// A range from 'a' to stop, begin() and end() are different
class CharRange : public DiffEndRange<char, CharInc> {
 public:
  constexpr CharRange(char stop) : DiffEndRange<char, CharInc>('a', stop) {}
};

struct IncIntCharPair {
  void operator()(std::pair<int, char>& p) {
    ++p.first;
    ++p.second;
  }
};

class IntCharPairRange
    : public DiffEndRange<std::pair<int, char>, IncIntCharPair> {
 public:
  IntCharPairRange(std::pair<int, char> stop)
      : DiffEndRange<std::pair<int, char>, IncIntCharPair>({0, 'a'}, stop) {}
};

#endif
<|endoftext|>