| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * testSudoku.cpp | 
					
						
							|  |  |  |  * @brief develop code for Sudoku CSP solver | 
					
						
							|  |  |  |  * @date Jan 29, 2012 | 
					
						
							|  |  |  |  * @author Frank Dellaert | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <CppUnitLite/TestHarness.h>
 | 
					
						
							| 
									
										
										
										
											2021-11-21 04:52:12 +08:00
										 |  |  | #include <gtsam/inference/Symbol.h>
 | 
					
						
							| 
									
										
										
										
											2021-11-18 23:54:00 +08:00
										 |  |  | #include <gtsam_unstable/discrete/CSP.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <stdarg.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | #include <iostream>
 | 
					
						
							|  |  |  | #include <sstream>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | using namespace std; | 
					
						
							|  |  |  | using namespace gtsam; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-25 22:51:03 +08:00
										 |  |  | #define PRINT false
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-21 04:52:12 +08:00
										 |  |  | /// A class that encodes Sudoku's as a CSP problem
 | 
					
						
							| 
									
										
										
										
											2021-11-18 23:54:00 +08:00
										 |  |  | class Sudoku : public CSP { | 
					
						
							| 
									
										
										
										
											2021-11-21 04:52:12 +08:00
										 |  |  |   size_t n_;  ///< Side of Sudoku, e.g. 4 or 9
 | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-21 04:52:12 +08:00
										 |  |  |   /// Mapping from base i,j coordinates to discrete keys:
 | 
					
						
							|  |  |  |   using IJ = std::pair<size_t, size_t>; | 
					
						
							| 
									
										
										
										
											2012-10-02 22:40:07 +08:00
										 |  |  |   std::map<IJ, DiscreteKey> dkeys_; | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-18 23:54:00 +08:00
										 |  |  |  public: | 
					
						
							| 
									
										
										
										
											2012-10-02 22:40:07 +08:00
										 |  |  |   /// return DiscreteKey for cell(i,j)
 | 
					
						
							|  |  |  |   const DiscreteKey& dkey(size_t i, size_t j) const { | 
					
						
							|  |  |  |     return dkeys_.at(IJ(i, j)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-09 00:35:28 +08:00
										 |  |  |   /// return Key for cell(i,j)
 | 
					
						
							| 
									
										
										
										
											2021-11-18 23:54:00 +08:00
										 |  |  |   Key key(size_t i, size_t j) const { return dkey(i, j).first; } | 
					
						
							| 
									
										
										
										
											2012-10-02 22:40:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /// Constructor
 | 
					
						
							| 
									
										
										
										
											2021-11-18 23:54:00 +08:00
										 |  |  |   Sudoku(size_t n, ...) : n_(n) { | 
					
						
							| 
									
										
										
										
											2012-10-02 22:40:07 +08:00
										 |  |  |     // Create variables, ordering, and unary constraints
 | 
					
						
							|  |  |  |     va_list ap; | 
					
						
							|  |  |  |     va_start(ap, n); | 
					
						
							|  |  |  |     for (size_t i = 0; i < n; ++i) { | 
					
						
							| 
									
										
										
										
											2021-11-21 04:52:12 +08:00
										 |  |  |       for (size_t j = 0; j < n; ++j) { | 
					
						
							| 
									
										
										
										
											2012-10-02 22:40:07 +08:00
										 |  |  |         // create the key
 | 
					
						
							|  |  |  |         IJ ij(i, j); | 
					
						
							| 
									
										
										
										
											2021-11-21 04:52:12 +08:00
										 |  |  |         Symbol key('1' + i, j + 1); | 
					
						
							|  |  |  |         dkeys_[ij] = DiscreteKey(key, n); | 
					
						
							| 
									
										
										
										
											2012-10-02 22:40:07 +08:00
										 |  |  |         // get the unary constraint, if any
 | 
					
						
							|  |  |  |         int value = va_arg(ap, int); | 
					
						
							|  |  |  |         if (value != 0) addSingleValue(dkeys_[ij], value - 1); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-11-18 23:54:00 +08:00
										 |  |  |       // cout << endl;
 | 
					
						
							| 
									
										
										
										
											2012-10-02 22:40:07 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |     va_end(ap); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // add row constraints
 | 
					
						
							|  |  |  |     for (size_t i = 0; i < n; i++) { | 
					
						
							|  |  |  |       DiscreteKeys dkeys; | 
					
						
							| 
									
										
										
										
											2023-01-08 02:19:52 +08:00
										 |  |  |       for (size_t j = 0; j < n; j++) dkeys.push_back(dkey(i, j)); | 
					
						
							| 
									
										
										
										
											2012-10-02 22:40:07 +08:00
										 |  |  |       addAllDiff(dkeys); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // add col constraints
 | 
					
						
							|  |  |  |     for (size_t j = 0; j < n; j++) { | 
					
						
							|  |  |  |       DiscreteKeys dkeys; | 
					
						
							| 
									
										
										
										
											2023-01-08 02:19:52 +08:00
										 |  |  |       for (size_t i = 0; i < n; i++) dkeys.push_back(dkey(i, j)); | 
					
						
							| 
									
										
										
										
											2012-10-02 22:40:07 +08:00
										 |  |  |       addAllDiff(dkeys); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // add box constraints
 | 
					
						
							|  |  |  |     size_t N = (size_t)sqrt(double(n)), i0 = 0; | 
					
						
							|  |  |  |     for (size_t I = 0; I < N; I++) { | 
					
						
							|  |  |  |       size_t j0 = 0; | 
					
						
							|  |  |  |       for (size_t J = 0; J < N; J++) { | 
					
						
							|  |  |  |         // Box I,J
 | 
					
						
							|  |  |  |         DiscreteKeys dkeys; | 
					
						
							|  |  |  |         for (size_t i = i0; i < i0 + N; i++) | 
					
						
							| 
									
										
										
										
											2023-01-08 02:19:52 +08:00
										 |  |  |           for (size_t j = j0; j < j0 + N; j++) dkeys.push_back(dkey(i, j)); | 
					
						
							| 
									
										
										
										
											2012-10-02 22:40:07 +08:00
										 |  |  |         addAllDiff(dkeys); | 
					
						
							|  |  |  |         j0 += N; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       i0 += N; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// Print readable form of assignment
 | 
					
						
							| 
									
										
										
										
											2021-12-14 02:46:53 +08:00
										 |  |  |   void printAssignment(const DiscreteValues& assignment) const { | 
					
						
							| 
									
										
										
										
											2012-10-02 22:40:07 +08:00
										 |  |  |     for (size_t i = 0; i < n_; i++) { | 
					
						
							|  |  |  |       for (size_t j = 0; j < n_; j++) { | 
					
						
							| 
									
										
										
										
											2013-11-09 00:35:28 +08:00
										 |  |  |         Key k = key(i, j); | 
					
						
							| 
									
										
										
										
											2021-11-21 05:15:05 +08:00
										 |  |  |         cout << 1 + assignment.at(k) << " "; | 
					
						
							| 
									
										
										
										
											2012-10-02 22:40:07 +08:00
										 |  |  |       } | 
					
						
							|  |  |  |       cout << endl; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /// solve and print solution
 | 
					
						
							| 
									
										
										
										
											2021-11-21 04:52:12 +08:00
										 |  |  |   void printSolution() const { | 
					
						
							| 
									
										
										
										
											2022-01-22 03:47:28 +08:00
										 |  |  |     auto MPE = optimize(); | 
					
						
							| 
									
										
										
										
											2012-10-02 22:40:07 +08:00
										 |  |  |     printAssignment(MPE); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-11-21 04:52:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Print domain
 | 
					
						
							|  |  |  |   void printDomains(const Domains& domains) { | 
					
						
							|  |  |  |     for (size_t i = 0; i < n_; i++) { | 
					
						
							|  |  |  |       for (size_t j = 0; j < n_; j++) { | 
					
						
							|  |  |  |         Key k = key(i, j); | 
					
						
							|  |  |  |         cout << domains.at(k).base1Str(); | 
					
						
							|  |  |  |         cout << "\t"; | 
					
						
							|  |  |  |       }  // i
 | 
					
						
							|  |  |  |       cout << endl; | 
					
						
							|  |  |  |     }  // j
 | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2021-11-21 05:44:17 +08:00
										 |  |  | TEST(Sudoku, small) { | 
					
						
							| 
									
										
										
										
											2021-11-19 00:31:11 +08:00
										 |  |  |   Sudoku csp(4,           //
 | 
					
						
							|  |  |  |              1, 0, 0, 4,  //
 | 
					
						
							|  |  |  |              0, 0, 0, 0,  //
 | 
					
						
							|  |  |  |              4, 0, 2, 0,  //
 | 
					
						
							|  |  |  |              0, 1, 0, 0); | 
					
						
							| 
									
										
										
										
											2012-10-02 22:40:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // optimize and check
 | 
					
						
							| 
									
										
										
										
											2022-01-22 03:47:28 +08:00
										 |  |  |   auto solution = csp.optimize(); | 
					
						
							| 
									
										
										
										
											2021-12-14 02:46:53 +08:00
										 |  |  |   DiscreteValues expected; | 
					
						
							| 
									
										
										
										
											2022-12-31 14:53:10 +08:00
										 |  |  |   expected = {{csp.key(0, 0), 0}, {csp.key(0, 1), 1}, | 
					
						
							|  |  |  |               {csp.key(0, 2), 2}, {csp.key(0, 3), 3},  //
 | 
					
						
							|  |  |  |               {csp.key(1, 0), 2}, {csp.key(1, 1), 3}, | 
					
						
							|  |  |  |               {csp.key(1, 2), 0}, {csp.key(1, 3), 1},  //
 | 
					
						
							|  |  |  |               {csp.key(2, 0), 3}, {csp.key(2, 1), 2}, | 
					
						
							|  |  |  |               {csp.key(2, 2), 1}, {csp.key(2, 3), 0},  //
 | 
					
						
							|  |  |  |               {csp.key(3, 0), 1}, {csp.key(3, 1), 0}, | 
					
						
							|  |  |  |               {csp.key(3, 2), 3}, {csp.key(3, 3), 2}}; | 
					
						
							| 
									
										
										
										
											2021-11-21 05:15:05 +08:00
										 |  |  |   EXPECT(assert_equal(expected, solution)); | 
					
						
							| 
									
										
										
										
											2021-11-18 23:54:00 +08:00
										 |  |  |   // csp.printAssignment(solution);
 | 
					
						
							| 
									
										
										
										
											2021-11-21 04:52:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Do BP (AC1)
 | 
					
						
							|  |  |  |   auto domains = csp.runArcConsistency(4, 3); | 
					
						
							|  |  |  |   // csp.printDomains(domains);
 | 
					
						
							|  |  |  |   Domain domain44 = domains.at(Symbol('4', 4)); | 
					
						
							|  |  |  |   EXPECT_LONGS_EQUAL(1, domain44.nrValues()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Test Creation of a new, simpler CSP
 | 
					
						
							|  |  |  |   CSP new_csp = csp.partiallyApply(domains); | 
					
						
							|  |  |  |   // Should only be 16 new Domains
 | 
					
						
							|  |  |  |   EXPECT_LONGS_EQUAL(16, new_csp.size()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Check that solution
 | 
					
						
							| 
									
										
										
										
											2022-01-22 03:47:28 +08:00
										 |  |  |   auto new_solution = new_csp.optimize(); | 
					
						
							| 
									
										
										
										
											2021-11-21 04:52:12 +08:00
										 |  |  |   // csp.printAssignment(new_solution);
 | 
					
						
							| 
									
										
										
										
											2021-11-21 05:44:17 +08:00
										 |  |  |   EXPECT(assert_equal(expected, new_solution)); | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2021-11-21 05:44:17 +08:00
										 |  |  | TEST(Sudoku, easy) { | 
					
						
							| 
									
										
										
										
											2021-11-21 04:52:12 +08:00
										 |  |  |   Sudoku csp(9,                          //
 | 
					
						
							|  |  |  |              0, 0, 5, 0, 9, 0, 0, 0, 1,  //
 | 
					
						
							|  |  |  |              0, 0, 0, 0, 0, 2, 0, 7, 3,  //
 | 
					
						
							|  |  |  |              7, 6, 0, 0, 0, 8, 2, 0, 0,  //
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |              0, 1, 2, 0, 0, 9, 0, 0, 4,  //
 | 
					
						
							|  |  |  |              0, 0, 0, 2, 0, 3, 0, 0, 0,  //
 | 
					
						
							|  |  |  |              3, 0, 0, 1, 0, 0, 9, 6, 0,  //
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |              0, 0, 1, 9, 0, 0, 0, 5, 8,  //
 | 
					
						
							|  |  |  |              9, 7, 0, 5, 0, 0, 0, 0, 0,  //
 | 
					
						
							|  |  |  |              5, 0, 0, 0, 3, 0, 7, 0, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // csp.printSolution(); // don't do it
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Do BP (AC1)
 | 
					
						
							|  |  |  |   auto domains = csp.runArcConsistency(9, 10); | 
					
						
							|  |  |  |   // csp.printDomains(domains);
 | 
					
						
							|  |  |  |   Key key99 = Symbol('9', 9); | 
					
						
							|  |  |  |   Domain domain99 = domains.at(key99); | 
					
						
							|  |  |  |   EXPECT_LONGS_EQUAL(1, domain99.nrValues()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Test Creation of a new, simpler CSP
 | 
					
						
							|  |  |  |   CSP new_csp = csp.partiallyApply(domains); | 
					
						
							|  |  |  |   // 81 new Domains, and still 26 all-diff constraints
 | 
					
						
							|  |  |  |   EXPECT_LONGS_EQUAL(81 + 26, new_csp.size()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // csp.printSolution(); // still don't do it ! :-(
 | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2021-11-21 05:44:17 +08:00
										 |  |  | TEST(Sudoku, extreme) { | 
					
						
							| 
									
										
										
										
											2021-11-21 04:52:12 +08:00
										 |  |  |   Sudoku csp(9,                             //
 | 
					
						
							|  |  |  |              0, 0, 9, 7, 4, 8, 0, 0, 0, 7,  //
 | 
					
						
							|  |  |  |              0, 0, 0, 0, 0, 0, 0, 0, 0, 2,  //
 | 
					
						
							|  |  |  |              0, 1, 0, 9, 0, 0, 0, 0, 0, 7,  //
 | 
					
						
							|  |  |  |              0, 0, 0, 2, 4, 0, 0, 6, 4, 0,  //
 | 
					
						
							|  |  |  |              1, 0, 5, 9, 0, 0, 9, 8, 0, 0,  //
 | 
					
						
							|  |  |  |              0, 3, 0, 0, 0, 0, 0, 8, 0, 3,  //
 | 
					
						
							|  |  |  |              0, 2, 0, 0, 0, 0, 0, 0, 0, 0,  //
 | 
					
						
							|  |  |  |              0, 6, 0, 0, 0, 2, 7, 5, 9, 0, 0); | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-02 22:40:07 +08:00
										 |  |  |   // Do BP
 | 
					
						
							| 
									
										
										
										
											2021-11-21 04:52:12 +08:00
										 |  |  |   csp.runArcConsistency(9, 10); | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | #ifdef METIS
 | 
					
						
							| 
									
										
										
										
											2021-11-21 04:52:12 +08:00
										 |  |  |   VariableIndexOrdered index(csp); | 
					
						
							| 
									
										
										
										
											2012-10-02 22:40:07 +08:00
										 |  |  |   index.print("index"); | 
					
						
							|  |  |  |   ofstream os("/Users/dellaert/src/hmetis-1.5-osx-i686/extreme-dual.txt"); | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  |   index.outputMetisFormat(os); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-21 04:52:12 +08:00
										 |  |  |   // Do BP (AC1)
 | 
					
						
							|  |  |  |   auto domains = csp.runArcConsistency(9, 10); | 
					
						
							|  |  |  |   // csp.printDomains(domains);
 | 
					
						
							|  |  |  |   Key key99 = Symbol('9', 9); | 
					
						
							|  |  |  |   Domain domain99 = domains.at(key99); | 
					
						
							|  |  |  |   EXPECT_LONGS_EQUAL(2, domain99.nrValues()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Test Creation of a new, simpler CSP
 | 
					
						
							|  |  |  |   CSP new_csp = csp.partiallyApply(domains); | 
					
						
							|  |  |  |   // 81 new Domains, and still 20 all-diff constraints
 | 
					
						
							|  |  |  |   EXPECT_LONGS_EQUAL(81 + 20, new_csp.size()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // csp.printSolution(); // still don't do it ! :-(
 | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2021-11-21 05:44:17 +08:00
										 |  |  | TEST(Sudoku, AJC_3star_Feb8_2012) { | 
					
						
							| 
									
										
										
										
											2021-11-21 04:52:12 +08:00
										 |  |  |   Sudoku csp(9,                          //
 | 
					
						
							|  |  |  |              9, 5, 0, 0, 0, 6, 0, 0, 0,  //
 | 
					
						
							|  |  |  |              0, 8, 4, 0, 7, 0, 0, 0, 0,  //
 | 
					
						
							|  |  |  |              6, 2, 0, 5, 0, 0, 4, 0, 0,  //
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |              0, 0, 0, 2, 9, 0, 6, 0, 0,  //
 | 
					
						
							|  |  |  |              0, 9, 0, 0, 0, 0, 0, 2, 0,  //
 | 
					
						
							|  |  |  |              0, 0, 2, 0, 6, 3, 0, 0, 0,  //
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |              0, 0, 9, 0, 0, 7, 0, 6, 8,  //
 | 
					
						
							|  |  |  |              0, 0, 0, 0, 3, 0, 2, 9, 0,  //
 | 
					
						
							|  |  |  |              0, 0, 0, 1, 0, 0, 0, 3, 7); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Do BP (AC1)
 | 
					
						
							|  |  |  |   auto domains = csp.runArcConsistency(9, 10); | 
					
						
							|  |  |  |   // csp.printDomains(domains);
 | 
					
						
							|  |  |  |   Key key99 = Symbol('9', 9); | 
					
						
							|  |  |  |   Domain domain99 = domains.at(key99); | 
					
						
							|  |  |  |   EXPECT_LONGS_EQUAL(1, domain99.nrValues()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Test Creation of a new, simpler CSP
 | 
					
						
							|  |  |  |   CSP new_csp = csp.partiallyApply(domains); | 
					
						
							|  |  |  |   // Just the 81 new Domains
 | 
					
						
							|  |  |  |   EXPECT_LONGS_EQUAL(81, new_csp.size()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Check that solution
 | 
					
						
							| 
									
										
										
										
											2022-01-22 03:47:28 +08:00
										 |  |  |   auto solution = new_csp.optimize(); | 
					
						
							| 
									
										
										
										
											2021-11-21 04:52:12 +08:00
										 |  |  |   // csp.printAssignment(solution);
 | 
					
						
							| 
									
										
										
										
											2021-11-21 05:44:17 +08:00
										 |  |  |   EXPECT_LONGS_EQUAL(6, solution.at(key99)); | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | int main() { | 
					
						
							| 
									
										
										
										
											2012-10-02 22:40:07 +08:00
										 |  |  |   TestResult tr; | 
					
						
							|  |  |  |   return TestRegistry::runAllTests(tr); | 
					
						
							| 
									
										
										
										
											2012-04-16 06:35:28 +08:00
										 |  |  | } | 
					
						
							|  |  |  | /* ************************************************************************* */ |