1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.
Jip J. Dekker 981be2067e Squashed 'software/gecode_on_replay/' content from commit 8051d92b9
git-subtree-dir: software/gecode_on_replay
git-subtree-split: 8051d92b9c89e49cccfbd1c201371580d7703ab4
2021-06-16 14:04:29 +10:00

948 lines
24 KiB
C++

/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
* Main authors:
* Mikael Lagerkvist <lagerkvist@gecode.org>
*
* Contributing authors:
* Guido Tack <tack@gecode.org>
*
* Copyright:
* Mikael Lagerkvist, 2006
* Guido Tack, 2006
*
* This file is part of Gecode, the generic constraint
* development environment:
* http://www.gecode.org
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <gecode/driver.hh>
#include <gecode/int.hh>
#include <gecode/minimodel.hh>
using namespace Gecode;
/** \brief Specification of one tile
*
* This structure can be used to specify a tile with specified width
* and height, number of such tiles (all with unique values), and a
* char-array tile showing the tile in row-major order.
*
* \relates Pentominoes
*/
class TileSpec {
public:
int width; ///< Width of tile
int height; ///< Height of tile
int amount; ///< Number of tiles
const char *tile; ///< Picture of tile
};
/** \brief Board specifications
*
* Each board specification repurposes the first two TileSpecs to
* record width and height of the board (TileSpec 0) as well as the
* number of tiles and whether the board is filled (TileSpec 1).
*
* \relates Pentominoes
*/
extern const TileSpec *examples[];
/** \brief Board specification sizes
*
* \relates Pentominoes
*/
extern const int examples_size[];
/** \brief Number of board specifications
*
* \relates Pentominoes
*/
extern const unsigned int n_examples;
namespace {
/** \name Symmetry functions
*
* These functions implement the 8 symmetries of 2D planes. The
* functions are templatized so that they can be used both for the
* pieces (defined using C-strings) and for arrays of variables.
*
* \relates Pentominoes
*/
//@{
/** Return index of (\a h, \a w) in the row-major layout of a matrix with
* width \a w1 and height \a h1.
*/
int pos(int h, int w, int h1, int w1);
/// Type for tile symmetry functions
typedef void (*tsymmfunc)(const char*, int, int, char*, int&, int&);
/// Type for board symmetry functions
typedef void (*bsymmfunc)(const IntVarArgs, int, int, IntVarArgs&, int&, int&);
/// Identity symmetry
template<class CArray, class Array>
void id(CArray t1, int w1, int h1, Array t2, int& w2, int&h2);
/// Rotate 90 degrees
template<class CArray, class Array>
void rot90(CArray t1, int w1, int h1, Array t2, int& w2, int& h2);
/// Rotate 180 degrees
template<class CArray, class Array>
void rot180(CArray t1, int w1, int h1, Array t2, int& w2, int& h2);
/// Rotate 270 degrees
template<class CArray, class Array>
void rot270(CArray t1, int w1, int h1, Array t2, int& w2, int& h2);
/// Flip x-wise
template<class CArray, class Array>
void flipx(CArray t1, int w1, int h1, Array t2, int& w2, int& h2);
/// Flip y-wise
template<class CArray, class Array>
void flipy(CArray t1, int w1, int h1, Array t2, int& w2, int& h2);
/// Flip diagonal 1
template<class CArray, class Array>
void flipd1(CArray t1, int w1, int h1, Array t2, int& w2, int& h2);
/// Flip diagonal 2
template<class CArray, class Array>
void flipd2(CArray t1, int w1, int h1, Array t2, int& w2, int& h2);
//@}
}
/**
* \brief %Example: %Pentominoes
*
* \section ScriptPentominoesProblem The Problem
*
* This example places pieces of a puzzle, where each piece is
* composed of a collection of squares, onto a grid. The pieces may
* all be rotated and flipped freely. The goal is to place all the
* pieces on the grid, without any overlaps. An example piece to be
* placed looks like
* \code
* XXX
* X
* XXX
* \endcode
* in one of its rotations.
*
* The most famous instance of such a puzzle is the Pentominoes
* puzzle, where the pieces are all pieces formed by 5 four-connected
* squares.
*
*
* \section ScriptPentominoesVariables The Variables
*
* The variables for the model is the grid of squares that the pieces
* are placed on, where each of the variables for the squares takes
* the value of the number of the piece which is placed overonto it.
*
*
* \section ScriptPentominoesOnePiece Placing one piece
*
* The constraint for each piece placement uses regular expressions
* (and consequently the extensional constraint) for expressing
* placement of (rotated) pieces on the grid. Consider the simple
* example of placing the piece
* \code
* XX
* X
* X
* \endcode
* onto the 4 by 4 board
* \code
* 0123
* 4567
* 89AB
* CDEF
* \endcode
*
* Let the variables 0-F be 0/1-variables indicating if the piece is
* placed on that position or not. First consider placing the piece on
* some location, say covering 1,2,6, and A. Then, writing the
* sequence of values for the variables 0-F out, we get the string
* 0110001000100000. This string and all other strings corresponding
* to placing the above piece in that particular rotation can be
* described using the regular expression \f$0^*11000100010^*\f$. The
* expression indicates that first comes some number of zeroes, then
* two ones in a row (covering places 1 and 2 in our example
* placement), then comes exactly three 0's (not covering places 3, 4,
* and 5), and so on. The variable number of 0's at the beginning and at the end
* makes the expression match any placement of the piece on the board.
*
* There is one problem with the above constraint, since it allows
* placing the piece covering places 3, 4, 8, and C. That is, the
* piece may wrap around the board. To prohibit this, we add a new
* dummy-column to the board, so that it looks like
* \code
* 0123G
* 4567H
* 89ABI
* CDEFJ
* \endcode
* The variables for places G to J are all set to zero initially, and the
* regular expression for the placement of the piece is modified to
* include the extra column, \f$0^*1100001000010^*\f$.
*
*
* \section ScriptPentominoesRotatingPiece Rotating pieces
*
* To handle rotations of the piece, we can use disjunctions of
* regular expressions for all the relevant rotations. Consider the
* rotated version of the above piece, depicted below.
* \code
* X
* XXX
* \endcode
* The corresponding regular expression for this piece is
* \f$0^*1001110^*\f$. To combine these two regular expressions, we
* can simply use disjunction of regular expressions, arriving at the
* expression \f$0^*1100001000010^*|0^*1001110^*\f$ for enforcing
* the placement of the piece in one of the above two rotations.
*
* There are 8 symmetries for the pieces in general. The 8 disjuncts
* for a particular piece might, however, contain less than 8 distinct
* expressions (for example, any square has only one distinct
* disjunct). This is removed when then automaton for the expression
* is computed, since it is minimized.
*
*
* \section ScriptPentominoesSeveral Placing several pieces
*
* To generalize the above model to several pieces, we let the
* variables range from 0 to n, where n is the number of pieces to
* place. Given that we place three pieces, and that the above shown
* piece is number one, we will replace each \f$0\f$-expression with
* the expression \f$(0|2|3)\f$. Thus, the second regular expression
* becomes \f$(0|2|3)^*1(0|2|3)(0|2|3)111(0|2|3)^*\f$. Additionaly,
* the end of line marker gets its own value.
*
* This generalization suffers from the fact that the automata become
* much more complex. This problem can be solved by instead
* projecting out each component, which gives a new board of
* 0/1-variables for each piece to place.
*
*
* \section ScriptPentominoesHeuristic The Branching
*
* This model does not use any advanced heuristic for the
* branching. The variables selection is simply in order, and the
* value selection is minimum value first.
*
* The static value selection allows us to order the pieces in the
* specification of the problem. The pieces are approximately ordered by
* largness or hardness to place.
*
*
* \section ScriptPentominoesSymmetries Removing board symmetries
*
* Especially when searching for all solutions of a puzzle instance,
* we might want to remove the symmetrical boards from the
* solutions-space. This is done using the same symmetry functions as
* for the piece symmetries and lexicographical order constraints.
*
*
* \ingroup Example
*
*/
class Pentominoes : public Script {
public:
/// Choice of propagators
enum {
PROPAGATION_INT, ///< Use integer propagators
PROPAGATION_BOOLEAN, ///< Use Boolean propagators
};
/// Choice of symmetry breaking
enum {
SYMMETRY_NONE, ///< Do not remove symmetric solutions
SYMMETRY_FULL, ///< Remove symmetric solutions
};
private:
/// Specification of the tiles to place.
const TileSpec *spec;
/// Width and height of the board
int width, height;
/// Whether the board is filled or not
bool filled;
/// Number of specifications of tiles to place
int nspecs;
/// Number of tiles to place
int ntiles;
/// The variables for the board.
IntVarArray board;
/// Compute number of tiles
int compute_number_of_tiles(const TileSpec* ts, int nspecs) const {
int res = 0;
for (int i=0; i<nspecs; i++)
res += ts[i].amount;
return res;
}
/// Returns the regular expression for placing a specific tile \a
/// tile in a specific rotation.
REG tile_reg(int twidth, int theight, const char* tile,
REG mark, REG other, REG eol) const {
REG oe = other | eol;
REG res = *oe;
REG color[] = {other, mark};
for (int h = 0; h < theight; ++h) {
for (int w = 0; w < twidth; ++w) {
int which = tile[h*twidth + w] == 'X';
res += color[which];
}
if (h < theight-1) {
res += oe(width-twidth, width-twidth);
}
}
res += *oe + oe;
return res;
}
/// Returns the regular expression for placing tile number \a t in
/// any rotation.
REG get_constraint(int t, REG mark, REG other, REG eol) {
// This should be done for all rotations
REG res;
char *t2 = new char[width*height];
int w2, h2;
tsymmfunc syms[] = {id, flipx, flipy, flipd1, flipd2, rot90, rot180, rot270};
int symscnt = sizeof(syms)/sizeof(tsymmfunc);
for (int i = 0; i < symscnt; ++i) {
syms[i](spec[t].tile, spec[t].width, spec[t].height, t2, w2, h2);
res = res | tile_reg(w2, h2, t2, mark, other, eol);
}
delete [] t2;
return res;
}
public:
/// Construction of the model.
Pentominoes(const SizeOptions& opt)
: Script(opt), spec(examples[opt.size()]),
width(spec[0].width+1), // Add one for extra row at end.
height(spec[0].height),
filled(spec[0].amount),
nspecs(examples_size[opt.size()]-1),
ntiles(compute_number_of_tiles(spec+1, nspecs)),
board(*this, width*height, filled,ntiles+1) {
spec += 1; // No need for the specification-part any longer
// Set end-of-line markers
for (int h = 0; h < height; ++h) {
for (int w = 0; w < width-1; ++w)
rel(*this, board[h*width + w], IRT_NQ, ntiles+1);
rel(*this, board[h*width + width - 1], IRT_EQ, ntiles+1);
}
// Post constraints
if (opt.propagation() == PROPAGATION_INT) {
int tile = 0;
for (int i = 0; i < nspecs; ++i) {
for (int j = 0; j < spec[i].amount; ++j) {
// Color
int col = tile+1;
// Expression for color col
REG mark(col);
// Build expression for complement to color col
REG other;
bool first = true;
for (int j = filled; j <= ntiles; ++j) {
if (j == col) continue;
if (first) {
other = REG(j);
first = false;
} else {
other |= REG(j);
}
}
// End of line marker
REG eol(ntiles+1);
extensional(*this, board, get_constraint(i, mark, other, eol));
++tile;
}
}
} else { // opt.propagation() == PROPAGATION_BOOLEAN
int ncolors = ntiles + 2;
// Boolean variables for channeling
BoolVarArgs p(*this,ncolors * board.size(),0,1);
// Post channel constraints
for (int i=board.size(); i--; ) {
BoolVarArgs c(ncolors);
for (int j=ncolors; j--; )
c[j]=p[i*ncolors+j];
channel(*this, c, board[i]);
}
// For placing tile i, we construct the expression over
// 0/1-variables and apply it to the projection of
// the board on the color for the tile.
REG other(0), mark(1);
int tile = 0;
for (int i = 0; i < nspecs; ++i) {
for (int j = 0; j < spec[i].amount; ++j) {
int col = tile+1;
// Projection for color col
BoolVarArgs c(board.size());
for (int k = board.size(); k--; ) {
c[k] = p[k*ncolors+col];
}
extensional(*this, c, get_constraint(i, mark, other, other));
++tile;
}
}
}
if (opt.symmetry() == SYMMETRY_FULL) {
// Remove symmetrical boards
IntVarArgs orig(board.size()-height), symm(board.size()-height);
int pos = 0;
for (int i = 0; i < board.size(); ++i) {
if ((i+1)%width==0) continue;
orig[pos++] = board[i];
}
int w2, h2;
bsymmfunc syms[] = {flipx, flipy, flipd1, flipd2, rot90, rot180, rot270};
int symscnt = sizeof(syms)/sizeof(bsymmfunc);
for (int i = 0; i < symscnt; ++i) {
syms[i](orig, width-1, height, symm, w2, h2);
if (width-1 == w2 && height == h2)
rel(*this, orig, IRT_LQ, symm);
}
}
// Install branching
branch(*this, board, INT_VAR_NONE(), INT_VAL_MIN());
}
/// Constructor for cloning \a s
Pentominoes(Pentominoes& s) :
Script(s), spec(s.spec), width(s.width), height(s.height),
filled(s.filled), nspecs(s.nspecs) {
board.update(*this, s.board);
}
/// Copy space during cloning
virtual Space*
copy(void) {
return new Pentominoes(*this);
}
/// Print solution
virtual void
print(std::ostream& os) const {
for (int h = 0; h < height; ++h) {
os << "\t";
for (int w = 0; w < width-1; ++w) {
int val = board[h*width + w].val();
char c = val < 10 ? '0'+val : 'A' + (val-10);
os << c;
}
os << std::endl;
}
os << std::endl;
}
};
/** \brief Main-function
* \relates Pentominoes
*/
int
main(int argc, char* argv[]) {
SizeOptions opt("Pentominoes");
opt.size(1);
opt.symmetry(Pentominoes::SYMMETRY_FULL);
opt.symmetry(Pentominoes::SYMMETRY_NONE,
"none", "do not remove symmetric solutions");
opt.symmetry(Pentominoes::SYMMETRY_FULL,
"full", "remove symmetric solutions");
opt.propagation(Pentominoes::PROPAGATION_BOOLEAN);
opt.propagation(Pentominoes::PROPAGATION_INT,
"int", "use integer propagators");
opt.propagation(Pentominoes::PROPAGATION_BOOLEAN,
"bool", "use Boolean propagators");
opt.parse(argc,argv);
if (opt.size() >= n_examples) {
std::cerr << "Error: size must be between 0 and "
<< n_examples-1 << std::endl;
return 1;
}
Script::run<Pentominoes,DFS,SizeOptions>(opt);
return 0;
}
/** \name Puzzle specifications
*
* \relates Pentominoes
*/
//@{
/// Small specification
static const TileSpec puzzle0[] =
{
// Width and height of board
{4, 4, true, ""},
{2, 3, 1,
"XX"
"X "
"X "},
{2, 1, 1,
"XX"},
{3, 3, 1,
" XX"
" X"
"XXX"},
{1, 1, 1,
"X"},
{3, 1, 1,
"XXX"}
};
/// Standard specification
static const TileSpec puzzle1[] =
{
// Width and height of board
{8, 8, true, ""},
{3, 3, 1,
"XXX"
"XXX"
"XX "},
{5, 3, 1,
" XXX"
" X "
"XXX "},
{3, 4, 1,
"XXX"
"XXX"
" X"
" X"},
{3, 4, 1,
"XXX"
" X"
" X"
" X"},
{2, 5, 1,
" X"
" X"
" X"
"XX"
"XX"},
{4, 2, 1,
"XX "
"XXXX"},
{3, 3, 1,
"XXX"
" X"
" X"},
{2, 3, 1,
"XX"
"X "
"X "},
{2, 4, 1,
"XX"
"XX"
"XX"
"XX"},
{3, 2, 1,
"XX "
"XXX"}
};
// Perfect square number 2 from examples/perfect-square.cc
static const TileSpec square2[] =
{
// Width and height of board
{10, 10, true, ""},
{6, 6, 1,
"XXXXXX"
"XXXXXX"
"XXXXXX"
"XXXXXX"
"XXXXXX"
"XXXXXX"
},
{4, 4, 3,
"XXXX"
"XXXX"
"XXXX"
"XXXX"},
{2, 2, 4,
"XX"
"XX"}
};
// Perfect square number 3 from examples/perfect-square.cc
static const TileSpec square3[] =
{
// Width and height of board
{20, 20, true, ""},
{9, 9, 1,
"XXXXXXXXX"
"XXXXXXXXX"
"XXXXXXXXX"
"XXXXXXXXX"
"XXXXXXXXX"
"XXXXXXXXX"
"XXXXXXXXX"
"XXXXXXXXX"
"XXXXXXXXX"
},
{8, 8, 2,
"XXXXXXXX"
"XXXXXXXX"
"XXXXXXXX"
"XXXXXXXX"
"XXXXXXXX"
"XXXXXXXX"
"XXXXXXXX"
"XXXXXXXX"
},
{7, 7, 1,
"XXXXXXX"
"XXXXXXX"
"XXXXXXX"
"XXXXXXX"
"XXXXXXX"
"XXXXXXX"
"XXXXXXX"
},
{5, 5, 1,
"XXXXX"
"XXXXX"
"XXXXX"
"XXXXX"
"XXXXX"
},
{4, 4, 5,
"XXXX"
"XXXX"
"XXXX"
"XXXX"},
{3, 3, 3,
"XXX"
"XXX"
"XXX"},
{2, 2, 2,
"XX"
"XX"},
{1, 1, 2,
"X"}
};
static const TileSpec pentomino6x10[] =
{
// Width and height of board
{10, 6, true, ""},
{2, 4, 1,
"X "
"X "
"X "
"XX"},
{3,3, 1,
"XX "
" XX"
" X "},
{3,3, 1,
"XXX"
" X "
" X "},
{3,3, 1,
" X"
" XX"
"XX "},
{2,4, 1,
" X"
"XX"
" X"
" X"},
{5,1, 1,
"XXXXX"},
{3,3, 1,
"X "
"XXX"
" X"},
{4,2, 1,
" XXX"
"XX "},
{2,3, 1,
"XX"
"XX"
" X"},
{3,2, 1,
"X X"
"XXX"},
{3,3, 1,
" X "
"XXX"
" X "},
{3,3, 1,
" X"
" X"
"XXX"}
};
static const TileSpec pentomino5x12[] =
{
// Width and height of board
{12, 5, true, ""},
{2, 4, 1,
"X "
"X "
"X "
"XX"},
{3,3, 1,
"XX "
" XX"
" X "},
{3,3, 1,
"XXX"
" X "
" X "},
{3,3, 1,
" X"
" XX"
"XX "},
{2,4, 1,
" X"
"XX"
" X"
" X"},
{5,1, 1,
"XXXXX"},
{3,3, 1,
"X "
"XXX"
" X"},
{4,2, 1,
" XXX"
"XX "},
{2,3, 1,
"XX"
"XX"
" X"},
{3,2, 1,
"X X"
"XXX"},
{3,3, 1,
" X "
"XXX"
" X "},
{3,3, 1,
" X"
" X"
"XXX"}
};
static const TileSpec pentomino4x15[] =
{
// Width and height of board
{15, 4, true, ""},
{2, 4, 1,
"X "
"X "
"X "
"XX"},
{3,3, 1,
"XX "
" XX"
" X "},
{3,3, 1,
"XXX"
" X "
" X "},
{3,3, 1,
" X"
" XX"
"XX "},
{2,4, 1,
" X"
"XX"
" X"
" X"},
{5,1, 1,
"XXXXX"},
{3,3, 1,
"X "
"XXX"
" X"},
{4,2, 1,
" XXX"
"XX "},
{2,3, 1,
"XX"
"XX"
" X"},
{3,2, 1,
"X X"
"XXX"},
{3,3, 1,
" X "
"XXX"
" X "},
{3,3, 1,
" X"
" X"
"XXX"}
};
static const TileSpec pentomino3x20[] =
{
// Width and height of board
{20, 3, true, ""},
{2, 4, 1,
"X "
"X "
"X "
"XX"},
{3,3, 1,
"XX "
" XX"
" X "},
{3,3, 1,
"XXX"
" X "
" X "},
{3,3, 1,
" X"
" XX"
"XX "},
{2,4, 1,
" X"
"XX"
" X"
" X"},
{5,1, 1,
"XXXXX"},
{3,3, 1,
"X "
"XXX"
" X"},
{4,2, 1,
" XXX"
"XX "},
{2,3, 1,
"XX"
"XX"
" X"},
{3,2, 1,
"X X"
"XXX"},
{3,3, 1,
" X "
"XXX"
" X "},
{3,3, 1,
" X"
" X"
"XXX"}
};
/// List of specifications
const TileSpec *examples[] = {puzzle0, puzzle1, square2, square3,
pentomino6x10,pentomino5x12,
pentomino4x15,pentomino3x20};
const int examples_size[] = {sizeof(puzzle0)/sizeof(TileSpec),
sizeof(puzzle1)/sizeof(TileSpec),
sizeof(square2)/sizeof(TileSpec),
sizeof(square3)/sizeof(TileSpec),
sizeof(pentomino6x10)/sizeof(TileSpec),
sizeof(pentomino5x12)/sizeof(TileSpec),
sizeof(pentomino4x15)/sizeof(TileSpec),
sizeof(pentomino3x20)/sizeof(TileSpec)};
/// Number of specifications
const unsigned n_examples = sizeof(examples)/sizeof(TileSpec*);
//@}
// Symmetry functions
namespace {
int pos(int h, int w, int h1, int w1) {
if (!(0 <= h && h < h1) ||
!(0 <= w && w < w1)) {
std::cerr << "Cannot place (" << h << "," << w
<< ") on board of size " << h1 << "x" << w1 << std::endl;
}
return h * w1 + w;
}
template<class CArray, class Array>
void id(CArray t1, int w1, int h1, Array t2, int& w2, int&h2) {
w2 = w1; h2 = h1;
for (int h = 0; h < h1; ++h)
for (int w = 0; w < w1; ++w)
t2[pos(h, w, h2, w2)] = t1[pos(h, w, h1, w1)];
}
template<class CArray, class Array>
void rot90(CArray t1, int w1, int h1, Array t2, int& w2, int& h2) {
w2 = h1; h2 = w1;
for (int h = 0; h < h1; ++h)
for (int w = 0; w < w1; ++w)
t2[pos(w, w2-h-1, h2, w2)] = t1[pos(h, w, h1, w1)];
}
template<class CArray, class Array>
void rot180(CArray t1, int w1, int h1, Array t2, int& w2, int& h2) {
w2 = w1; h2 = h1;
for (int h = 0; h < h1; ++h)
for (int w = 0; w < w1; ++w)
t2[pos(h2-h-1, w2-w-1, h2, w2)] = t1[pos(h, w, h1, w1)];
}
template<class CArray, class Array>
void rot270(CArray t1, int w1, int h1, Array t2, int& w2, int& h2) {
w2 = h1; h2 = w1;
for (int h = 0; h < h1; ++h)
for (int w = 0; w < w1; ++w)
t2[pos(h2-w-1, h, h2, w2)] = t1[pos(h, w, h1, w1)];
}
template<class CArray, class Array>
void flipx(CArray t1, int w1, int h1, Array t2, int& w2, int& h2) {
w2 = w1; h2 = h1;
for (int h = 0; h < h1; ++h)
for (int w = 0; w < w1; ++w)
t2[pos(h, w2-w-1, h2, w2)] = t1[pos(h, w, h1, w1)];
}
template<class CArray, class Array>
void flipy(CArray t1, int w1, int h1, Array t2, int& w2, int& h2) {
w2 = w1; h2 = h1;
for (int h = 0; h < h1; ++h)
for (int w = 0; w < w1; ++w)
t2[pos(h2-h-1, w, h2, w2)] = t1[pos(h, w, h1, w1)];
}
template<class CArray, class Array>
void flipd1(CArray t1, int w1, int h1, Array t2, int& w2, int& h2) {
w2 = h1; h2 = w1;
for (int h = 0; h < h1; ++h)
for (int w = 0; w < w1; ++w)
t2[pos(w, h, h2, w2)] = t1[pos(h, w, h1, w1)];
}
template<class CArray, class Array>
void flipd2(CArray t1, int w1, int h1, Array t2, int& w2, int& h2) {
w2 = h1; h2 = w1;
for (int h = 0; h < h1; ++h)
for (int w = 0; w < w1; ++w)
t2[pos(h2-w-1, w2-h-1, h2, w2)] = t1[pos(h, w, h1, w1)];
}
}
// STATISTICS: example-any