Generated on Tue May 22 09:39:39 2018 for Gecode by doxygen 1.6.3

pentominoes.cpp

Go to the documentation of this file.
00001 /* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
00002 /*
00003  *  Main authors:
00004  *     Mikael Lagerkvist <lagerkvist@gecode.org>
00005  *
00006  *  Contributing authors:
00007  *     Guido Tack <tack@gecode.org>
00008  *
00009  *  Copyright:
00010  *     Mikael Lagerkvist, 2006
00011  *     Guido Tack, 2006
00012  *
00013  *  This file is part of Gecode, the generic constraint
00014  *  development environment:
00015  *     http://www.gecode.org
00016  *
00017  *  Permission is hereby granted, free of charge, to any person obtaining
00018  *  a copy of this software and associated documentation files (the
00019  *  "Software"), to deal in the Software without restriction, including
00020  *  without limitation the rights to use, copy, modify, merge, publish,
00021  *  distribute, sublicense, and/or sell copies of the Software, and to
00022  *  permit persons to whom the Software is furnished to do so, subject to
00023  *  the following conditions:
00024  *
00025  *  The above copyright notice and this permission notice shall be
00026  *  included in all copies or substantial portions of the Software.
00027  *
00028  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00029  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00030  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00031  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
00032  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
00033  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00034  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00035  *
00036  */
00037 
00038 #include <gecode/driver.hh>
00039 #include <gecode/int.hh>
00040 #include <gecode/minimodel.hh>
00041 
00042 using namespace Gecode;
00043 
00052 class TileSpec {
00053 public:
00054   int width;  
00055   int height; 
00056   int amount; 
00057   const char *tile; 
00058 };
00059 
00068 extern const TileSpec *examples[];
00073 extern const int examples_size[];
00078 extern const unsigned int n_examples;
00079 
00080 namespace {
00093   int pos(int h, int w, int h1, int w1);
00095   typedef void (*tsymmfunc)(const char*, int, int, char*, int&, int&);
00097   typedef void (*bsymmfunc)(const IntVarArgs, int, int, IntVarArgs&, int&, int&);
00099   template<class CArray, class Array>
00100   void id(CArray t1, int w1, int h1, Array t2, int& w2, int&h2);
00102   template<class CArray, class Array>
00103   void rot90(CArray t1, int w1, int h1, Array t2, int& w2, int& h2);
00105   template<class CArray, class Array>
00106   void rot180(CArray t1, int w1, int h1, Array t2, int& w2, int& h2);
00108   template<class CArray, class Array>
00109   void rot270(CArray t1, int w1, int h1, Array t2, int& w2, int& h2);
00111   template<class CArray, class Array>
00112   void flipx(CArray t1, int w1, int h1, Array t2, int& w2, int& h2);
00114   template<class CArray, class Array>
00115   void flipy(CArray t1, int w1, int h1, Array t2, int& w2, int& h2);
00117   template<class CArray, class Array>
00118   void flipd1(CArray t1, int w1, int h1, Array t2, int& w2, int& h2);
00120   template<class CArray, class Array>
00121   void flipd2(CArray t1, int w1, int h1, Array t2, int& w2, int& h2);
00123 }
00124 
00261 class Pentominoes : public Script {
00262 public:
00264   enum {
00265     PROPAGATION_INT,       
00266     PROPAGATION_BOOLEAN,   
00267   };
00269   enum {
00270     SYMMETRY_NONE, 
00271     SYMMETRY_FULL, 
00272   };
00273 private:
00275   const TileSpec *spec;
00277   int width, height;
00279   bool filled;
00281   int nspecs;
00283   int ntiles;
00284 
00286   IntVarArray board;
00287 
00289   int compute_number_of_tiles(const TileSpec* ts, int nspecs) {
00290     int res = 0;
00291     for (int i = nspecs; i--; ) {
00292       res += ts[i].amount;
00293     }
00294     return res;
00295   }
00296 
00299   REG tile_reg(int twidth, int theight, const char* tile,
00300                REG mark, REG other, REG eol) {
00301     REG oe = other | eol;
00302     REG res = *oe;
00303     REG color[] = {other, mark};
00304     for (int h = 0; h < theight; ++h) {
00305       for (int w = 0; w < twidth; ++w) {
00306         int which = tile[h*twidth + w] == 'X';
00307         res += color[which];
00308       }
00309       if (h < theight-1) {
00310         res += oe(width-twidth, width-twidth);
00311       }
00312     }
00313     res += *oe + oe;
00314     return res;
00315   }
00316 
00319   REG get_constraint(int t, REG mark, REG other, REG eol) {
00320     // This should be done for all rotations
00321     REG res;
00322     char *t2 = new char[width*height];
00323     int w2, h2;
00324     tsymmfunc syms[] = {id, flipx, flipy, flipd1, flipd2, rot90, rot180, rot270};
00325     int symscnt = sizeof(syms)/sizeof(tsymmfunc);
00326     for (int i = 0; i < symscnt; ++i) {
00327       syms[i](spec[t].tile, spec[t].width, spec[t].height, t2, w2, h2);
00328       res = res | tile_reg(w2, h2, t2, mark, other, eol);
00329     }
00330     delete [] t2;
00331 
00332     return res;
00333   }
00334 
00335 
00336 public:
00338   Pentominoes(const SizeOptions& opt)
00339     : Script(opt), spec(examples[opt.size()]),
00340       width(spec[0].width+1), // Add one for extra row at end.
00341       height(spec[0].height),
00342       filled(spec[0].amount),
00343       nspecs(examples_size[opt.size()]-1),
00344       ntiles(compute_number_of_tiles(spec+1, nspecs)),
00345       board(*this, width*height, filled,ntiles+1) {
00346     spec += 1; // No need for the specification-part any longer
00347 
00348     // Set end-of-line markers
00349     for (int h = 0; h < height; ++h) {
00350       for (int w = 0; w < width-1; ++w)
00351         rel(*this, board[h*width + w], IRT_NQ, ntiles+1);
00352       rel(*this, board[h*width + width - 1], IRT_EQ, ntiles+1);
00353     }
00354 
00355     // Post constraints
00356     if (opt.propagation() == PROPAGATION_INT) {
00357       int tile = 0;
00358       for (int i = 0; i < nspecs; ++i) {
00359         for (int j = 0; j < spec[i].amount; ++j) {
00360           // Color
00361           int col = tile+1;
00362           // Expression for color col
00363           REG mark(col);
00364           // Build expression for complement to color col
00365           REG other;
00366           bool first = true;
00367           for (int j = filled; j <= ntiles; ++j) {
00368             if (j == col) continue;
00369             if (first) {
00370               other = REG(j);
00371               first = false;
00372             } else {
00373               other |= REG(j);
00374             }
00375           }
00376           // End of line marker
00377           REG eol(ntiles+1);
00378           extensional(*this, board, get_constraint(i, mark, other, eol));
00379           ++tile;
00380         }
00381       }
00382     } else { // opt.propagation() == PROPAGATION_BOOLEAN
00383       int ncolors = ntiles + 2;
00384       // Boolean variables for channeling
00385       BoolVarArgs p(*this,ncolors * board.size(),0,1);
00386 
00387       // Post channel constraints
00388       for (int i=board.size(); i--; ) {
00389         BoolVarArgs c(ncolors);
00390         for (int j=ncolors; j--; )
00391           c[j]=p[i*ncolors+j];
00392         channel(*this, c, board[i]);
00393       }
00394 
00395       // For placing tile i, we construct the expression over
00396       // 0/1-variables and apply it to the projection of
00397       // the board on the color for the tile.
00398       REG other(0), mark(1);
00399       int tile = 0;
00400       for (int i = 0; i < nspecs; ++i) {
00401         for (int j = 0; j < spec[i].amount; ++j) {
00402           int col = tile+1;
00403           // Projection for color col
00404           BoolVarArgs c(board.size());
00405 
00406           for (int k = board.size(); k--; ) {
00407             c[k] = p[k*ncolors+col];
00408           }
00409 
00410           extensional(*this, c, get_constraint(i, mark, other, other));
00411           ++tile;
00412         }
00413       }
00414     }
00415 
00416     if (opt.symmetry() == SYMMETRY_FULL) {
00417       // Remove symmetrical boards
00418       IntVarArgs orig(board.size()-height), symm(board.size()-height);
00419       int pos = 0;
00420       for (int i = 0; i < board.size(); ++i) {
00421         if ((i+1)%width==0) continue;
00422         orig[pos++] = board[i];
00423       }
00424 
00425       int w2, h2;
00426       bsymmfunc syms[] = {flipx, flipy, flipd1, flipd2, rot90, rot180, rot270};
00427       int symscnt = sizeof(syms)/sizeof(bsymmfunc);
00428       for (int i = 0; i < symscnt; ++i) {
00429         syms[i](orig, width-1, height, symm, w2, h2);
00430         if (width-1 == w2 && height == h2)
00431           rel(*this, orig, IRT_LQ, symm);
00432       }
00433     }
00434 
00435     // Install branching
00436     branch(*this, board, INT_VAR_NONE(), INT_VAL_MIN());
00437   }
00438 
00440   Pentominoes(Pentominoes& s) :
00441     Script(s), spec(s.spec), width(s.width), height(s.height),
00442     filled(s.filled), nspecs(s.nspecs) {
00443     board.update(*this, s.board);
00444   }
00445 
00447   virtual Space*
00448   copy(void) {
00449     return new Pentominoes(*this);
00450   }
00451 
00453   virtual void
00454   print(std::ostream& os) const {
00455     for (int h = 0; h < height; ++h) {
00456       os << "\t";
00457       for (int w = 0; w < width-1; ++w) {
00458         int val =  board[h*width + w].val();
00459         char c = val < 10 ? '0'+val : 'A' + (val-10);
00460         os << c;
00461       }
00462       os << std::endl;
00463     }
00464     os << std::endl;
00465   }
00466 };
00467 
00468 
00472 int
00473 main(int argc, char* argv[]) {
00474   SizeOptions opt("Pentominoes");
00475   opt.size(1);
00476   opt.symmetry(Pentominoes::SYMMETRY_FULL);
00477   opt.symmetry(Pentominoes::SYMMETRY_NONE,
00478             "none", "do not remove symmetric solutions");
00479   opt.symmetry(Pentominoes::SYMMETRY_FULL,
00480             "full", "remove symmetric solutions");
00481 
00482   opt.propagation(Pentominoes::PROPAGATION_BOOLEAN);
00483   opt.propagation(Pentominoes::PROPAGATION_INT,
00484                   "int", "use integer propagators");
00485   opt.propagation(Pentominoes::PROPAGATION_BOOLEAN,
00486                   "bool", "use Boolean propagators");
00487 
00488   opt.parse(argc,argv);
00489   if (opt.size() >= n_examples) {
00490     std::cerr << "Error: size must be between 0 and "
00491               << n_examples-1 << std::endl;
00492     return 1;
00493   }
00494   Script::run<Pentominoes,DFS,SizeOptions>(opt);
00495   return 0;
00496 }
00497 
00498 
00504 
00505 static const TileSpec puzzle0[] =
00506   {
00507     // Width and height of board
00508     {4, 4, true, ""},
00509     {2, 3, 1,
00510      "XX"
00511      "X "
00512      "X "},
00513     {2, 1, 1,
00514      "XX"},
00515     {3, 3, 1,
00516      " XX"
00517      "  X"
00518      "XXX"},
00519     {1, 1, 1,
00520      "X"},
00521     {3, 1, 1,
00522      "XXX"}
00523   };
00525 static const TileSpec puzzle1[] =
00526   {
00527     // Width and height of board
00528     {8, 8, true, ""},
00529     {3, 3, 1,
00530      "XXX"
00531      "XXX"
00532      "XX "},
00533     {5, 3, 1,
00534      "  XXX"
00535      "  X  "
00536      "XXX  "},
00537     {3, 4, 1,
00538      "XXX"
00539      "XXX"
00540      "  X"
00541      "  X"},
00542     {3, 4, 1,
00543      "XXX"
00544      "  X"
00545      "  X"
00546      "  X"},
00547     {2, 5, 1,
00548      " X"
00549      " X"
00550      " X"
00551      "XX"
00552      "XX"},
00553     {4, 2, 1,
00554      "XX  "
00555      "XXXX"},
00556     {3, 3, 1,
00557      "XXX"
00558      "  X"
00559      "  X"},
00560     {2, 3, 1,
00561      "XX"
00562      "X "
00563      "X "},
00564     {2, 4, 1,
00565      "XX"
00566      "XX"
00567      "XX"
00568      "XX"},
00569     {3, 2, 1,
00570      "XX "
00571      "XXX"}
00572   };
00573 
00574 // Perfect square number 2 from examples/perfect-square.cc
00575 static const TileSpec square2[] =
00576   {
00577     // Width and height of board
00578     {10, 10, true, ""},
00579     {6, 6, 1,
00580      "XXXXXX"
00581      "XXXXXX"
00582      "XXXXXX"
00583      "XXXXXX"
00584      "XXXXXX"
00585      "XXXXXX"
00586     },
00587     {4, 4, 3,
00588      "XXXX"
00589      "XXXX"
00590      "XXXX"
00591      "XXXX"},
00592     {2, 2, 4,
00593      "XX"
00594      "XX"}
00595   };
00596 
00597 // Perfect square number 3 from examples/perfect-square.cc
00598 static const TileSpec square3[] =
00599   {
00600     // Width and height of board
00601     {20, 20, true, ""},
00602     {9, 9, 1,
00603      "XXXXXXXXX"
00604      "XXXXXXXXX"
00605      "XXXXXXXXX"
00606      "XXXXXXXXX"
00607      "XXXXXXXXX"
00608      "XXXXXXXXX"
00609      "XXXXXXXXX"
00610      "XXXXXXXXX"
00611      "XXXXXXXXX"
00612     },
00613     {8, 8, 2,
00614      "XXXXXXXX"
00615      "XXXXXXXX"
00616      "XXXXXXXX"
00617      "XXXXXXXX"
00618      "XXXXXXXX"
00619      "XXXXXXXX"
00620      "XXXXXXXX"
00621      "XXXXXXXX"
00622     },
00623     {7, 7, 1,
00624      "XXXXXXX"
00625      "XXXXXXX"
00626      "XXXXXXX"
00627      "XXXXXXX"
00628      "XXXXXXX"
00629      "XXXXXXX"
00630      "XXXXXXX"
00631     },
00632     {5, 5, 1,
00633      "XXXXX"
00634      "XXXXX"
00635      "XXXXX"
00636      "XXXXX"
00637      "XXXXX"
00638     },
00639     {4, 4, 5,
00640      "XXXX"
00641      "XXXX"
00642      "XXXX"
00643      "XXXX"},
00644     {3, 3, 3,
00645      "XXX"
00646      "XXX"
00647      "XXX"},
00648     {2, 2, 2,
00649      "XX"
00650      "XX"},
00651     {1, 1, 2,
00652      "X"}
00653   };
00654 
00655 static const TileSpec pentomino6x10[] =
00656   {
00657     // Width and height of board
00658     {10, 6, true, ""},
00659     {2, 4, 1,
00660      "X "
00661      "X "
00662      "X "
00663      "XX"},
00664     {3,3, 1,
00665      "XX "
00666      " XX"
00667      " X "},
00668     {3,3, 1,
00669      "XXX"
00670      " X "
00671      " X "},
00672     {3,3, 1,
00673      "  X"
00674      " XX"
00675      "XX "},
00676     {2,4, 1,
00677      " X"
00678      "XX"
00679      " X"
00680      " X"},
00681     {5,1, 1,
00682      "XXXXX"},
00683     {3,3, 1,
00684      "X  "
00685      "XXX"
00686      "  X"},
00687     {4,2, 1,
00688      " XXX"
00689      "XX  "},
00690     {2,3, 1,
00691      "XX"
00692      "XX"
00693      " X"},
00694     {3,2, 1,
00695      "X X"
00696      "XXX"},
00697     {3,3, 1,
00698      " X "
00699      "XXX"
00700      " X "},
00701     {3,3, 1,
00702      "  X"
00703      "  X"
00704      "XXX"}
00705   };
00706 
00707 static const TileSpec pentomino5x12[] =
00708   {
00709     // Width and height of board
00710     {12, 5, true, ""},
00711     {2, 4, 1,
00712      "X "
00713      "X "
00714      "X "
00715      "XX"},
00716     {3,3, 1,
00717      "XX "
00718      " XX"
00719      " X "},
00720     {3,3, 1,
00721      "XXX"
00722      " X "
00723      " X "},
00724     {3,3, 1,
00725      "  X"
00726      " XX"
00727      "XX "},
00728     {2,4, 1,
00729      " X"
00730      "XX"
00731      " X"
00732      " X"},
00733     {5,1, 1,
00734      "XXXXX"},
00735     {3,3, 1,
00736      "X  "
00737      "XXX"
00738      "  X"},
00739     {4,2, 1,
00740      " XXX"
00741      "XX  "},
00742     {2,3, 1,
00743      "XX"
00744      "XX"
00745      " X"},
00746     {3,2, 1,
00747      "X X"
00748      "XXX"},
00749     {3,3, 1,
00750      " X "
00751      "XXX"
00752      " X "},
00753     {3,3, 1,
00754      "  X"
00755      "  X"
00756      "XXX"}
00757   };
00758 
00759 static const TileSpec pentomino4x15[] =
00760   {
00761     // Width and height of board
00762     {15, 4, true, ""},
00763     {2, 4, 1,
00764      "X "
00765      "X "
00766      "X "
00767      "XX"},
00768     {3,3, 1,
00769      "XX "
00770      " XX"
00771      " X "},
00772     {3,3, 1,
00773      "XXX"
00774      " X "
00775      " X "},
00776     {3,3, 1,
00777      "  X"
00778      " XX"
00779      "XX "},
00780     {2,4, 1,
00781      " X"
00782      "XX"
00783      " X"
00784      " X"},
00785     {5,1, 1,
00786      "XXXXX"},
00787     {3,3, 1,
00788      "X  "
00789      "XXX"
00790      "  X"},
00791     {4,2, 1,
00792      " XXX"
00793      "XX  "},
00794     {2,3, 1,
00795      "XX"
00796      "XX"
00797      " X"},
00798     {3,2, 1,
00799      "X X"
00800      "XXX"},
00801     {3,3, 1,
00802      " X "
00803      "XXX"
00804      " X "},
00805     {3,3, 1,
00806      "  X"
00807      "  X"
00808      "XXX"}
00809   };
00810 
00811 static const TileSpec pentomino3x20[] =
00812   {
00813     // Width and height of board
00814     {20, 3, true, ""},
00815     {2, 4, 1,
00816      "X "
00817      "X "
00818      "X "
00819      "XX"},
00820     {3,3, 1,
00821      "XX "
00822      " XX"
00823      " X "},
00824     {3,3, 1,
00825      "XXX"
00826      " X "
00827      " X "},
00828     {3,3, 1,
00829      "  X"
00830      " XX"
00831      "XX "},
00832     {2,4, 1,
00833      " X"
00834      "XX"
00835      " X"
00836      " X"},
00837     {5,1, 1,
00838      "XXXXX"},
00839     {3,3, 1,
00840      "X  "
00841      "XXX"
00842      "  X"},
00843     {4,2, 1,
00844      " XXX"
00845      "XX  "},
00846     {2,3, 1,
00847      "XX"
00848      "XX"
00849      " X"},
00850     {3,2, 1,
00851      "X X"
00852      "XXX"},
00853     {3,3, 1,
00854      " X "
00855      "XXX"
00856      " X "},
00857     {3,3, 1,
00858      "  X"
00859      "  X"
00860      "XXX"}
00861   };
00862 
00864 const TileSpec *examples[] = {puzzle0, puzzle1, square2, square3,
00865                            pentomino6x10,pentomino5x12,
00866                            pentomino4x15,pentomino3x20};
00867 const int examples_size[] = {sizeof(puzzle0)/sizeof(TileSpec),
00868                              sizeof(puzzle1)/sizeof(TileSpec),
00869                              sizeof(square2)/sizeof(TileSpec),
00870                              sizeof(square3)/sizeof(TileSpec),
00871                              sizeof(pentomino6x10)/sizeof(TileSpec),
00872                              sizeof(pentomino5x12)/sizeof(TileSpec),
00873                              sizeof(pentomino4x15)/sizeof(TileSpec),
00874                              sizeof(pentomino3x20)/sizeof(TileSpec)};
00875 
00877 const unsigned n_examples = sizeof(examples)/sizeof(TileSpec*);
00879 
00880 // Symmetry functions
00881 namespace {
00882   int pos(int h, int w, int h1, int w1) {
00883     if (!(0 <= h && h < h1) ||
00884         !(0 <= w && w < w1)) {
00885       std::cerr << "Cannot place (" << h << "," << w
00886                 << ") on board of size " << h1 << "x" << w1 << std::endl;
00887     }
00888     return h * w1 + w;
00889   }
00890   template<class CArray, class Array>
00891   void id(CArray t1, int w1, int h1, Array t2, int& w2, int&h2) {
00892     w2 = w1; h2 = h1;
00893     for (int h = 0; h < h1; ++h)
00894       for (int w = 0; w < w1; ++w)
00895         t2[pos(h, w, h2, w2)] = t1[pos(h, w, h1, w1)];
00896   }
00897   template<class CArray, class Array>
00898   void rot90(CArray t1, int w1, int h1, Array t2, int& w2, int& h2) {
00899     w2 = h1; h2 = w1;
00900     for (int h = 0; h < h1; ++h)
00901       for (int w = 0; w < w1; ++w)
00902         t2[pos(w, w2-h-1, h2, w2)] = t1[pos(h, w, h1, w1)];
00903   }
00904   template<class CArray, class Array>
00905   void rot180(CArray t1, int w1, int h1, Array t2, int& w2, int& h2) {
00906     w2 = w1; h2 = h1;
00907     for (int h = 0; h < h1; ++h)
00908       for (int w = 0; w < w1; ++w)
00909         t2[pos(h2-h-1, w2-w-1, h2, w2)] = t1[pos(h, w, h1, w1)];
00910   }
00911   template<class CArray, class Array>
00912   void rot270(CArray t1, int w1, int h1, Array t2, int& w2, int& h2) {
00913     w2 = h1; h2 = w1;
00914     for (int h = 0; h < h1; ++h)
00915       for (int w = 0; w < w1; ++w)
00916         t2[pos(h2-w-1, h, h2, w2)] = t1[pos(h, w, h1, w1)];
00917   }
00918   template<class CArray, class Array>
00919   void flipx(CArray t1, int w1, int h1, Array t2, int& w2, int& h2) {
00920     w2 = w1; h2 = h1;
00921     for (int h = 0; h < h1; ++h)
00922       for (int w = 0; w < w1; ++w)
00923         t2[pos(h, w2-w-1, h2, w2)] = t1[pos(h, w, h1, w1)];
00924   }
00925   template<class CArray, class Array>
00926   void flipy(CArray t1, int w1, int h1, Array t2, int& w2, int& h2) {
00927     w2 = w1; h2 = h1;
00928     for (int h = 0; h < h1; ++h)
00929       for (int w = 0; w < w1; ++w)
00930         t2[pos(h2-h-1, w, h2, w2)] = t1[pos(h, w, h1, w1)];
00931   }
00932   template<class CArray, class Array>
00933   void flipd1(CArray t1, int w1, int h1, Array t2, int& w2, int& h2) {
00934     w2 = h1; h2 = w1;
00935     for (int h = 0; h < h1; ++h)
00936       for (int w = 0; w < w1; ++w)
00937         t2[pos(w, h, h2, w2)] = t1[pos(h, w, h1, w1)];
00938   }
00939   template<class CArray, class Array>
00940   void flipd2(CArray t1, int w1, int h1, Array t2, int& w2, int& h2) {
00941     w2 = h1; h2 = w1;
00942     for (int h = 0; h < h1; ++h)
00943       for (int w = 0; w < w1; ++w)
00944         t2[pos(h2-w-1, w2-h-1, h2, w2)] = t1[pos(h, w, h1, w1)];
00945   }
00946 }
00947 
00948 // STATISTICS: example-any