2007-10-12 13:15:25 +08:00
|
|
|
// This file is part of Eigen, a lightweight C++ template library
|
2009-05-23 02:25:33 +08:00
|
|
|
// for linear algebra.
|
2007-09-05 18:42:15 +08:00
|
|
|
//
|
2008-11-24 21:40:43 +08:00
|
|
|
// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com>
|
2008-03-26 17:13:11 +08:00
|
|
|
// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr>
|
2007-09-05 18:42:15 +08:00
|
|
|
//
|
2008-02-28 23:44:45 +08:00
|
|
|
// Eigen is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU Lesser General Public
|
2008-03-09 03:02:24 +08:00
|
|
|
// License as published by the Free Software Foundation; either
|
2008-02-28 23:44:45 +08:00
|
|
|
// version 3 of the License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// Alternatively, you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU General Public License as
|
2008-03-09 03:02:24 +08:00
|
|
|
// published by the Free Software Foundation; either version 2 of
|
2008-02-28 23:44:45 +08:00
|
|
|
// the License, or (at your option) any later version.
|
2007-09-05 18:42:15 +08:00
|
|
|
//
|
2007-10-12 13:15:25 +08:00
|
|
|
// Eigen is distributed in the hope that it will be useful, but WITHOUT ANY
|
2007-09-05 18:42:15 +08:00
|
|
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
2008-02-28 23:44:45 +08:00
|
|
|
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the
|
|
|
|
// GNU General Public License for more details.
|
2007-09-05 18:42:15 +08:00
|
|
|
//
|
2008-03-09 03:02:24 +08:00
|
|
|
// You should have received a copy of the GNU Lesser General Public
|
2008-02-28 23:44:45 +08:00
|
|
|
// License and a copy of the GNU General Public License along with
|
|
|
|
// Eigen. If not, see <http://www.gnu.org/licenses/>.
|
2007-09-05 18:42:15 +08:00
|
|
|
|
2007-09-09 17:41:15 +08:00
|
|
|
#include <cstdlib>
|
|
|
|
#include <ctime>
|
2008-03-13 02:10:52 +08:00
|
|
|
#include <iostream>
|
2008-05-22 20:18:55 +08:00
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#ifndef EIGEN_TEST_FUNC
|
|
|
|
#error EIGEN_TEST_FUNC must be defined
|
|
|
|
#endif
|
2007-09-05 18:42:15 +08:00
|
|
|
|
2008-04-27 04:28:27 +08:00
|
|
|
#define DEFAULT_REPEAT 10
|
2007-12-03 16:35:23 +08:00
|
|
|
|
2008-05-22 20:18:55 +08:00
|
|
|
namespace Eigen
|
|
|
|
{
|
|
|
|
static std::vector<std::string> g_test_stack;
|
|
|
|
static int g_repeat;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define EI_PP_MAKE_STRING2(S) #S
|
|
|
|
#define EI_PP_MAKE_STRING(S) EI_PP_MAKE_STRING2(S)
|
|
|
|
|
2009-02-04 01:50:35 +08:00
|
|
|
#define EIGEN_DEFAULT_IO_FORMAT IOFormat(4, AlignCols, " ", "\n", "", "", "", "")
|
2008-05-22 20:18:55 +08:00
|
|
|
|
2008-03-09 03:02:24 +08:00
|
|
|
#ifndef EIGEN_NO_ASSERTION_CHECKING
|
|
|
|
|
|
|
|
namespace Eigen
|
|
|
|
{
|
|
|
|
static const bool should_raise_an_assert = false;
|
2008-03-13 02:10:52 +08:00
|
|
|
|
2008-06-02 22:54:52 +08:00
|
|
|
// Used to avoid to raise two exceptions at a time in which
|
2008-08-31 21:32:29 +08:00
|
|
|
// case the exception is not properly caught.
|
2008-03-13 02:10:52 +08:00
|
|
|
// This may happen when a second exceptions is raise in a destructor.
|
|
|
|
static bool no_more_assert = false;
|
|
|
|
|
2008-03-13 02:44:42 +08:00
|
|
|
struct ei_assert_exception
|
|
|
|
{
|
|
|
|
ei_assert_exception(void) {}
|
|
|
|
~ei_assert_exception() { Eigen::no_more_assert = false; }
|
|
|
|
};
|
2008-03-09 03:02:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// If EIGEN_DEBUG_ASSERTS is defined and if no assertion is raised while
|
|
|
|
// one should have been, then the list of excecuted assertions is printed out.
|
|
|
|
//
|
|
|
|
// EIGEN_DEBUG_ASSERTS is not enabled by default as it
|
|
|
|
// significantly increases the compilation time
|
|
|
|
// and might even introduce side effects that would hide
|
|
|
|
// some memory errors.
|
|
|
|
#ifdef EIGEN_DEBUG_ASSERTS
|
|
|
|
|
|
|
|
namespace Eigen
|
|
|
|
{
|
|
|
|
static bool ei_push_assert = false;
|
|
|
|
static std::vector<std::string> ei_assert_list;
|
|
|
|
}
|
|
|
|
|
2008-04-09 20:31:55 +08:00
|
|
|
#define ei_assert(a) \
|
2008-03-13 17:51:18 +08:00
|
|
|
if( (!(a)) && (!no_more_assert) ) \
|
|
|
|
{ \
|
|
|
|
Eigen::no_more_assert = true; \
|
|
|
|
throw Eigen::ei_assert_exception(); \
|
|
|
|
} \
|
|
|
|
else if (Eigen::ei_push_assert) \
|
|
|
|
{ \
|
|
|
|
ei_assert_list.push_back(std::string(EI_PP_MAKE_STRING(__FILE__)" ("EI_PP_MAKE_STRING(__LINE__)") : "#a) ); \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define VERIFY_RAISES_ASSERT(a) \
|
|
|
|
{ \
|
2008-06-02 22:54:52 +08:00
|
|
|
Eigen::no_more_assert = false; \
|
2008-03-13 17:51:18 +08:00
|
|
|
try { \
|
|
|
|
Eigen::ei_assert_list.clear(); \
|
|
|
|
Eigen::ei_push_assert = true; \
|
|
|
|
a; \
|
|
|
|
Eigen::ei_push_assert = false; \
|
2008-05-22 20:18:55 +08:00
|
|
|
std::cerr << "One of the following asserts should have been raised:\n"; \
|
2008-03-13 17:51:18 +08:00
|
|
|
for (uint ai=0 ; ai<ei_assert_list.size() ; ++ai) \
|
2008-05-22 20:18:55 +08:00
|
|
|
std::cerr << " " << ei_assert_list[ai] << "\n"; \
|
2008-06-02 22:54:52 +08:00
|
|
|
VERIFY(Eigen::should_raise_an_assert && # a); \
|
2008-03-13 17:51:18 +08:00
|
|
|
} catch (Eigen::ei_assert_exception e) { \
|
2008-06-02 22:54:52 +08:00
|
|
|
Eigen::ei_push_assert = false; VERIFY(true); \
|
2008-03-13 17:51:18 +08:00
|
|
|
} \
|
|
|
|
}
|
2008-03-09 03:02:24 +08:00
|
|
|
|
|
|
|
#else // EIGEN_DEBUG_ASSERTS
|
|
|
|
|
2008-06-02 22:54:52 +08:00
|
|
|
#define ei_assert(a) \
|
2008-03-13 17:51:18 +08:00
|
|
|
if( (!(a)) && (!no_more_assert) ) \
|
|
|
|
{ \
|
|
|
|
Eigen::no_more_assert = true; \
|
|
|
|
throw Eigen::ei_assert_exception(); \
|
2008-03-13 02:10:52 +08:00
|
|
|
}
|
2008-03-09 03:02:24 +08:00
|
|
|
|
2008-06-02 22:54:52 +08:00
|
|
|
#define VERIFY_RAISES_ASSERT(a) { \
|
|
|
|
Eigen::no_more_assert = false; \
|
|
|
|
try { a; VERIFY(Eigen::should_raise_an_assert && # a); } \
|
|
|
|
catch (Eigen::ei_assert_exception e) { VERIFY(true); } \
|
2008-03-13 17:51:18 +08:00
|
|
|
}
|
2008-03-09 03:02:24 +08:00
|
|
|
|
|
|
|
#endif // EIGEN_DEBUG_ASSERTS
|
|
|
|
|
2008-03-26 17:13:11 +08:00
|
|
|
#define EIGEN_USE_CUSTOM_ASSERT
|
2008-03-09 03:02:24 +08:00
|
|
|
|
|
|
|
#else // EIGEN_NO_ASSERTION_CHECKING
|
|
|
|
|
|
|
|
#define VERIFY_RAISES_ASSERT(a) {}
|
|
|
|
|
|
|
|
#endif // EIGEN_NO_ASSERTION_CHECKING
|
|
|
|
|
|
|
|
|
|
|
|
#define EIGEN_INTERNAL_DEBUGGING
|
2008-09-02 01:31:21 +08:00
|
|
|
#define EIGEN_NICE_RANDOM
|
2008-12-08 05:40:53 +08:00
|
|
|
#include <Eigen/Array>
|
2009-05-17 22:07:12 +08:00
|
|
|
#include <Eigen/QR> // required for createRandomMatrixOfRank
|
2008-03-09 03:02:24 +08:00
|
|
|
|
2008-06-01 02:11:48 +08:00
|
|
|
|
2008-05-22 20:18:55 +08:00
|
|
|
#define VERIFY(a) do { if (!(a)) { \
|
|
|
|
std::cerr << "Test " << g_test_stack.back() << " failed in "EI_PP_MAKE_STRING(__FILE__) << " (" << EI_PP_MAKE_STRING(__LINE__) << ")" \
|
|
|
|
<< std::endl << " " << EI_PP_MAKE_STRING(a) << std::endl << std::endl; \
|
|
|
|
exit(2); \
|
|
|
|
} } while (0)
|
|
|
|
|
|
|
|
#define VERIFY_IS_APPROX(a, b) VERIFY(test_ei_isApprox(a, b))
|
|
|
|
#define VERIFY_IS_NOT_APPROX(a, b) VERIFY(!test_ei_isApprox(a, b))
|
|
|
|
#define VERIFY_IS_MUCH_SMALLER_THAN(a, b) VERIFY(test_ei_isMuchSmallerThan(a, b))
|
|
|
|
#define VERIFY_IS_NOT_MUCH_SMALLER_THAN(a, b) VERIFY(!test_ei_isMuchSmallerThan(a, b))
|
|
|
|
#define VERIFY_IS_APPROX_OR_LESS_THAN(a, b) VERIFY(test_ei_isApproxOrLessThan(a, b))
|
|
|
|
#define VERIFY_IS_NOT_APPROX_OR_LESS_THAN(a, b) VERIFY(!test_ei_isApproxOrLessThan(a, b))
|
|
|
|
|
|
|
|
#define CALL_SUBTEST(FUNC) do { \
|
|
|
|
g_test_stack.push_back(EI_PP_MAKE_STRING(FUNC)); \
|
|
|
|
FUNC; \
|
|
|
|
g_test_stack.pop_back(); \
|
|
|
|
} while (0)
|
2007-12-05 15:22:22 +08:00
|
|
|
|
2007-12-03 02:32:59 +08:00
|
|
|
namespace Eigen {
|
2007-09-05 18:42:15 +08:00
|
|
|
|
2007-12-03 18:23:08 +08:00
|
|
|
template<typename T> inline typename NumTraits<T>::Real test_precision();
|
|
|
|
template<> inline int test_precision<int>() { return 0; }
|
2008-12-19 04:36:25 +08:00
|
|
|
template<> inline float test_precision<float>() { return 1e-3f; }
|
2008-08-23 01:48:36 +08:00
|
|
|
template<> inline double test_precision<double>() { return 1e-6; }
|
2007-12-03 18:23:08 +08:00
|
|
|
template<> inline float test_precision<std::complex<float> >() { return test_precision<float>(); }
|
|
|
|
template<> inline double test_precision<std::complex<double> >() { return test_precision<double>(); }
|
2008-09-14 19:59:10 +08:00
|
|
|
template<> inline long double test_precision<long double>() { return 1e-6; }
|
2007-12-03 18:23:08 +08:00
|
|
|
|
2008-02-28 20:38:12 +08:00
|
|
|
inline bool test_ei_isApprox(const int& a, const int& b)
|
|
|
|
{ return ei_isApprox(a, b, test_precision<int>()); }
|
|
|
|
inline bool test_ei_isMuchSmallerThan(const int& a, const int& b)
|
|
|
|
{ return ei_isMuchSmallerThan(a, b, test_precision<int>()); }
|
|
|
|
inline bool test_ei_isApproxOrLessThan(const int& a, const int& b)
|
|
|
|
{ return ei_isApproxOrLessThan(a, b, test_precision<int>()); }
|
|
|
|
|
|
|
|
inline bool test_ei_isApprox(const float& a, const float& b)
|
|
|
|
{ return ei_isApprox(a, b, test_precision<float>()); }
|
|
|
|
inline bool test_ei_isMuchSmallerThan(const float& a, const float& b)
|
|
|
|
{ return ei_isMuchSmallerThan(a, b, test_precision<float>()); }
|
|
|
|
inline bool test_ei_isApproxOrLessThan(const float& a, const float& b)
|
|
|
|
{ return ei_isApproxOrLessThan(a, b, test_precision<float>()); }
|
|
|
|
|
|
|
|
inline bool test_ei_isApprox(const double& a, const double& b)
|
|
|
|
{ return ei_isApprox(a, b, test_precision<double>()); }
|
|
|
|
inline bool test_ei_isMuchSmallerThan(const double& a, const double& b)
|
|
|
|
{ return ei_isMuchSmallerThan(a, b, test_precision<double>()); }
|
|
|
|
inline bool test_ei_isApproxOrLessThan(const double& a, const double& b)
|
|
|
|
{ return ei_isApproxOrLessThan(a, b, test_precision<double>()); }
|
|
|
|
|
|
|
|
inline bool test_ei_isApprox(const std::complex<float>& a, const std::complex<float>& b)
|
|
|
|
{ return ei_isApprox(a, b, test_precision<std::complex<float> >()); }
|
|
|
|
inline bool test_ei_isMuchSmallerThan(const std::complex<float>& a, const std::complex<float>& b)
|
|
|
|
{ return ei_isMuchSmallerThan(a, b, test_precision<std::complex<float> >()); }
|
|
|
|
|
|
|
|
inline bool test_ei_isApprox(const std::complex<double>& a, const std::complex<double>& b)
|
|
|
|
{ return ei_isApprox(a, b, test_precision<std::complex<double> >()); }
|
|
|
|
inline bool test_ei_isMuchSmallerThan(const std::complex<double>& a, const std::complex<double>& b)
|
|
|
|
{ return ei_isMuchSmallerThan(a, b, test_precision<std::complex<double> >()); }
|
2007-12-03 18:23:08 +08:00
|
|
|
|
2008-09-14 19:59:10 +08:00
|
|
|
inline bool test_ei_isApprox(const long double& a, const long double& b)
|
|
|
|
{ return ei_isApprox(a, b, test_precision<long double>()); }
|
|
|
|
inline bool test_ei_isMuchSmallerThan(const long double& a, const long double& b)
|
|
|
|
{ return ei_isMuchSmallerThan(a, b, test_precision<long double>()); }
|
|
|
|
inline bool test_ei_isApproxOrLessThan(const long double& a, const long double& b)
|
|
|
|
{ return ei_isApproxOrLessThan(a, b, test_precision<long double>()); }
|
|
|
|
|
2008-10-26 07:10:21 +08:00
|
|
|
template<typename Type1, typename Type2>
|
|
|
|
inline bool test_ei_isApprox(const Type1& a, const Type2& b)
|
2007-12-03 18:23:08 +08:00
|
|
|
{
|
2008-10-26 07:10:21 +08:00
|
|
|
return a.isApprox(b, test_precision<typename Type1::Scalar>());
|
2007-12-03 18:23:08 +08:00
|
|
|
}
|
|
|
|
|
2008-03-11 01:23:11 +08:00
|
|
|
template<typename Derived1, typename Derived2>
|
|
|
|
inline bool test_ei_isMuchSmallerThan(const MatrixBase<Derived1>& m1,
|
|
|
|
const MatrixBase<Derived2>& m2)
|
2007-12-03 18:23:08 +08:00
|
|
|
{
|
2008-03-13 01:17:36 +08:00
|
|
|
return m1.isMuchSmallerThan(m2, test_precision<typename ei_traits<Derived1>::Scalar>());
|
2007-12-03 18:23:08 +08:00
|
|
|
}
|
|
|
|
|
2008-03-11 01:23:11 +08:00
|
|
|
template<typename Derived>
|
|
|
|
inline bool test_ei_isMuchSmallerThan(const MatrixBase<Derived>& m,
|
2008-03-13 01:17:36 +08:00
|
|
|
const typename NumTraits<typename ei_traits<Derived>::Scalar>::Real& s)
|
2007-12-03 18:23:08 +08:00
|
|
|
{
|
2008-03-13 01:17:36 +08:00
|
|
|
return m.isMuchSmallerThan(s, test_precision<typename ei_traits<Derived>::Scalar>());
|
2007-12-03 18:23:08 +08:00
|
|
|
}
|
|
|
|
|
2009-05-17 22:07:12 +08:00
|
|
|
template<typename Derived>
|
|
|
|
void createRandomMatrixOfRank(int desired_rank, int rows, int cols, Eigen::MatrixBase<Derived>& m)
|
|
|
|
{
|
|
|
|
typedef Derived MatrixType;
|
2009-05-18 04:17:45 +08:00
|
|
|
typedef typename ei_traits<MatrixType>::Scalar Scalar;
|
2009-05-17 22:07:12 +08:00
|
|
|
typedef Matrix<Scalar, MatrixType::ColsAtCompileTime, 1> VectorType;
|
|
|
|
|
|
|
|
MatrixType a = MatrixType::Random(rows,rows);
|
|
|
|
MatrixType d = MatrixType::Identity(rows,cols);
|
|
|
|
MatrixType b = MatrixType::Random(cols,cols);
|
|
|
|
|
|
|
|
// set the diagonal such that only desired_rank non-zero entries reamain
|
|
|
|
const int diag_size = std::min(d.rows(),d.cols());
|
|
|
|
d.diagonal().segment(desired_rank, diag_size-desired_rank) = VectorType::Zero(diag_size-desired_rank);
|
|
|
|
|
2009-07-06 23:12:10 +08:00
|
|
|
HouseholderQR<MatrixType> qra(a);
|
|
|
|
HouseholderQR<MatrixType> qrb(b);
|
2009-05-17 22:07:12 +08:00
|
|
|
m = (qra.matrixQ() * d * qrb.matrixQ()).lazy();
|
|
|
|
}
|
|
|
|
|
2008-05-22 20:18:55 +08:00
|
|
|
} // end namespace Eigen
|
|
|
|
|
2008-10-26 07:10:21 +08:00
|
|
|
template<typename T> struct GetDifferentType;
|
|
|
|
|
|
|
|
template<> struct GetDifferentType<float> { typedef double type; };
|
|
|
|
template<> struct GetDifferentType<double> { typedef float type; };
|
|
|
|
template<typename T> struct GetDifferentType<std::complex<T> >
|
|
|
|
{ typedef std::complex<typename GetDifferentType<T>::type> type; };
|
2008-05-22 20:18:55 +08:00
|
|
|
|
|
|
|
// forward declaration of the main test function
|
2009-01-28 03:08:20 +08:00
|
|
|
void EIGEN_CAT(test_,EIGEN_TEST_FUNC)();
|
2008-05-22 20:18:55 +08:00
|
|
|
|
|
|
|
using namespace Eigen;
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
2007-09-05 18:42:15 +08:00
|
|
|
{
|
2008-05-22 20:18:55 +08:00
|
|
|
bool has_set_repeat = false;
|
|
|
|
bool has_set_seed = false;
|
|
|
|
bool need_help = false;
|
|
|
|
unsigned int seed = 0;
|
|
|
|
int repeat = DEFAULT_REPEAT;
|
|
|
|
|
2008-05-29 11:37:16 +08:00
|
|
|
for(int i = 1; i < argc; i++)
|
2008-05-22 20:18:55 +08:00
|
|
|
{
|
2008-05-29 11:37:16 +08:00
|
|
|
if(argv[i][0] == 'r')
|
2008-05-22 20:18:55 +08:00
|
|
|
{
|
|
|
|
if(has_set_repeat)
|
|
|
|
{
|
2008-09-04 06:35:45 +08:00
|
|
|
std::cout << "Argument " << argv[i] << " conflicting with a former argument" << std::endl;
|
2008-05-22 20:18:55 +08:00
|
|
|
return 1;
|
|
|
|
}
|
2008-05-29 11:37:16 +08:00
|
|
|
repeat = atoi(argv[i]+1);
|
2008-05-22 20:18:55 +08:00
|
|
|
has_set_repeat = true;
|
|
|
|
if(repeat <= 0)
|
|
|
|
{
|
2008-09-04 06:35:45 +08:00
|
|
|
std::cout << "Invalid \'repeat\' value " << argv[i]+1 << std::endl;
|
2008-05-22 20:18:55 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2008-05-29 11:37:16 +08:00
|
|
|
else if(argv[i][0] == 's')
|
2008-05-22 20:18:55 +08:00
|
|
|
{
|
|
|
|
if(has_set_seed)
|
|
|
|
{
|
2008-09-04 06:35:45 +08:00
|
|
|
std::cout << "Argument " << argv[i] << " conflicting with a former argument" << std::endl;
|
2008-05-22 20:18:55 +08:00
|
|
|
return 1;
|
|
|
|
}
|
2008-12-18 01:50:43 +08:00
|
|
|
seed = int(strtoul(argv[i]+1, 0, 10));
|
2008-05-22 20:18:55 +08:00
|
|
|
has_set_seed = true;
|
2008-05-29 11:37:16 +08:00
|
|
|
bool ok = seed!=0;
|
2008-05-22 20:18:55 +08:00
|
|
|
if(!ok)
|
|
|
|
{
|
2008-09-04 06:35:45 +08:00
|
|
|
std::cout << "Invalid \'seed\' value " << argv[i]+1 << std::endl;
|
2008-05-22 20:18:55 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
need_help = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(need_help)
|
|
|
|
{
|
2008-09-04 06:35:45 +08:00
|
|
|
std::cout << "This test application takes the following optional arguments:" << std::endl;
|
|
|
|
std::cout << " rN Repeat each test N times (default: " << DEFAULT_REPEAT << ")" << std::endl;
|
|
|
|
std::cout << " sN Use N as seed for random numbers (default: based on current time)" << std::endl;
|
2008-05-22 20:18:55 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!has_set_seed) seed = (unsigned int) time(NULL);
|
|
|
|
if(!has_set_repeat) repeat = DEFAULT_REPEAT;
|
|
|
|
|
2008-09-04 06:35:45 +08:00
|
|
|
std::cout << "Initializing random number generator with seed " << seed << std::endl;
|
2008-05-22 20:18:55 +08:00
|
|
|
srand(seed);
|
2008-09-04 06:35:45 +08:00
|
|
|
std::cout << "Repeating each test " << repeat << " times" << std::endl;
|
2008-05-22 20:18:55 +08:00
|
|
|
|
|
|
|
Eigen::g_repeat = repeat;
|
|
|
|
Eigen::g_test_stack.push_back(EI_PP_MAKE_STRING(EIGEN_TEST_FUNC));
|
|
|
|
|
2009-01-28 03:08:20 +08:00
|
|
|
EIGEN_CAT(test_,EIGEN_TEST_FUNC)();
|
2008-05-22 20:18:55 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-09-05 18:42:15 +08:00
|
|
|
|
2007-12-03 02:32:59 +08:00
|
|
|
|