本文整理汇总了C++中Teuchos::getFancyOStream方法的典型用法代码示例。如果您正苦于以下问题:C++ Teuchos::getFancyOStream方法的具体用法?C++ Teuchos::getFancyOStream怎么用?C++ Teuchos::getFancyOStream使用的例子?那么恭喜您, 这里精选的方法代码示例或许可以为您提供帮助。您也可以进一步了解该方法所在类Teuchos
的用法示例。
在下文中一共展示了Teuchos::getFancyOStream方法的8个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的C++代码示例。
示例1: build_precond
Teuchos::RCP<Tpetra::Operator<Scalar,LocalOrdinal,GlobalOrdinal,Node> >
build_precond (Teuchos::ParameterList& test_params,
const Teuchos::RCP<const Tpetra::CrsMatrix<Scalar,LocalOrdinal,GlobalOrdinal,Node> >& A)
{
using Teuchos::FancyOStream;
using Teuchos::getFancyOStream;
using Teuchos::OSTab;
using Teuchos::RCP;
using Teuchos::rcpFromRef;
using std::cout;
using std::endl;
typedef Tpetra::RowMatrix<Scalar,LocalOrdinal,GlobalOrdinal,Node> row_matrix_type;
Teuchos::Time timer("precond");
const int myRank = A->getRowMap ()->getComm ()->getRank ();
RCP<FancyOStream> out = getFancyOStream (rcpFromRef (cout));
typedef Ifpack2::Preconditioner<Scalar,LocalOrdinal,GlobalOrdinal,Node> Tprec;
Teuchos::RCP<Tprec> prec;
Ifpack2::Factory factory;
std::string prec_name("not specified");
Ifpack2::getParameter(test_params, "Ifpack2::Preconditioner", prec_name);
prec = factory.create<row_matrix_type> (prec_name, A);
Teuchos::ParameterList tif_params;
if (test_params.isSublist("Ifpack2")) {
tif_params = test_params.sublist("Ifpack2");
}
if (myRank == 0) {
*out << "Configuring, initializing, and computing Ifpack2 preconditioner" << endl;
}
{
OSTab tab (*out);
prec->setParameters (tif_params);
prec->initialize ();
{
Teuchos::TimeMonitor timeMon (timer);
prec->compute ();
}
if (myRank == 0) {
*out << "Finished computing Ifpack2 preconditioner" << endl;
OSTab tab2 (*out);
*out << "Time (s): " << timer.totalElapsedTime () << endl;
}
}
if (myRank == 0) {
*out << "Preconditioner attributes:" << endl;
OSTab tab (*out);
prec->describe (*out, Teuchos::VERB_LOW);
}
return prec;
}
示例2: getFancyOStream
void
DistObjectKA<Packet,LocalOrdinal,GlobalOrdinal,Node>::print (std::ostream &os) const
{
using Teuchos::FancyOStream;
using Teuchos::getFancyOStream;
using Teuchos::RCP;
using Teuchos::rcpFromRef;
using std::endl;
RCP<FancyOStream> out = getFancyOStream (rcpFromRef (os));
this->describe (*out, Teuchos::VERB_DEFAULT);
}
示例3: main
int
main (int argc, char *argv[])
{
using Teuchos::Comm;
using Teuchos::FancyOStream;
using Teuchos::getFancyOStream;
using Teuchos::oblackholestream;
using Teuchos::OSTab;
using Teuchos::ParameterList;
using Teuchos::parameterList;
using Teuchos::RCP;
using Teuchos::rcpFromRef;
using std::cout;
using std::endl;
//
// Typedefs for Tpetra template arguments.
//
typedef double scalar_type;
typedef long int global_ordinal_type;
typedef int local_ordinal_type;
typedef Kokkos::DefaultNode::DefaultNodeType node_type;
//
// Tpetra objects which are the MV and OP template parameters of the
// Belos specialization which we are testing.
//
typedef Tpetra::MultiVector<scalar_type, local_ordinal_type, global_ordinal_type, node_type> MV;
typedef Tpetra::Operator<scalar_type, local_ordinal_type, global_ordinal_type, node_type> OP;
//
// Other typedefs.
//
typedef Teuchos::ScalarTraits<scalar_type> STS;
typedef Tpetra::CrsMatrix<scalar_type, local_ordinal_type, global_ordinal_type, node_type> sparse_matrix_type;
Teuchos::GlobalMPISession mpiSession (&argc, &argv, &cout);
RCP<const Comm<int> > comm = Tpetra::DefaultPlatform::getDefaultPlatform().getComm();
RCP<node_type> node = Tpetra::DefaultPlatform::getDefaultPlatform().getNode();
RCP<oblackholestream> blackHole (new oblackholestream);
const int myRank = comm->getRank();
// Output stream that prints only on Rank 0.
RCP<FancyOStream> out;
if (myRank == 0) {
out = Teuchos::getFancyOStream (rcpFromRef (cout));
} else {
out = Teuchos::getFancyOStream (blackHole);
}
//
// Get test parameters from command-line processor.
//
// CommandLineProcessor always understands int, but may not
// understand global_ordinal_type. We convert to the latter below.
int numRows = comm->getSize() * 100;
bool tolerant = false;
bool verbose = false;
bool debug = false;
Teuchos::CommandLineProcessor cmdp (false, true);
cmdp.setOption("numRows", &numRows,
"Global number of rows (and columns) in the sparse matrix to generate.");
cmdp.setOption("tolerant", "intolerant", &tolerant,
"Whether to parse files tolerantly.");
cmdp.setOption("verbose", "quiet", &verbose,
"Print messages and results.");
cmdp.setOption("debug", "release", &debug,
"Run debugging checks and print copious debugging output.");
if (cmdp.parse(argc,argv) != Teuchos::CommandLineProcessor::PARSE_SUCCESSFUL) {
*out << "\nEnd Result: TEST FAILED" << endl;
return EXIT_FAILURE;
}
// Output stream for verbose output.
RCP<FancyOStream> verbOut = verbose ? out : getFancyOStream (blackHole);
const bool success = true;
// Test whether it's possible to instantiate the solver.
// This is a minimal compilation test.
*verbOut << "Instantiating Block GCRODR solver" << endl;
Belos::BlockGCRODRSolMgr<scalar_type, MV, OP> solver;
//
// Test setting solver parameters. For now, we just use an empty
// (but non-null) parameter list, which the solver should fill in
// with defaults.
//
*verbOut << "Setting solver parameters" << endl;
RCP<ParameterList> solverParams = parameterList ();
solver.setParameters (solverParams);
//
// Create a linear system to solve.
//
*verbOut << "Creating linear system" << endl;
RCP<sparse_matrix_type> A;
RCP<MV> X_guess, X_exact, B;
{
typedef Belos::Tpetra::ProblemMaker<sparse_matrix_type> factory_type;
factory_type factory (comm, node, out, tolerant, debug);
RCP<ParameterList> problemParams = parameterList ();
problemParams->set ("Global number of rows",
static_cast<global_ordinal_type> (numRows));
problemParams->set ("Problem type", std::string ("Nonsymmetric"));
//.........这里部分代码省略.........
示例4: print
void Export<LocalOrdinal,GlobalOrdinal,Node>::
print (std::ostream& os) const
{
using Teuchos::Comm;
using Teuchos::getFancyOStream;
using Teuchos::RCP;
using Teuchos::rcpFromRef;
using Teuchos::toString;
using std::endl;
RCP<const Comm<int> > comm = getSourceMap ()->getComm ();
const int myImageID = comm->getRank ();
const int numImages = comm->getSize ();
for (int imageCtr = 0; imageCtr < numImages; ++imageCtr) {
if (myImageID == imageCtr) {
os << endl;
if (myImageID == 0) { // I'm the root node (only output this info once)
os << "Export Data Members:" << endl;
}
os << "Image ID : " << myImageID << endl;
os << "permuteFromLIDs: " << toString (getPermuteFromLIDs ()) << endl;
os << "permuteToLIDs : " << toString (getPermuteToLIDs ()) << endl;
os << "remoteLIDs : " << toString (getRemoteLIDs ()) << endl;
os << "exportLIDs : " << toString (getExportLIDs ()) << endl;
os << "exportPIDs : " << toString (getExportPIDs ()) << endl;
os << "numSameIDs : " << getNumSameIDs () << endl;
os << "numPermuteIDs : " << getNumPermuteIDs () << endl;
os << "numRemoteIDs : " << getNumRemoteIDs () << endl;
os << "numExportIDs : " << getNumExportIDs () << endl;
}
// A few global barriers give output a chance to complete.
comm->barrier();
comm->barrier();
comm->barrier();
}
if (myImageID == 0) {
os << endl << endl << "Source Map:" << endl << std::flush;
}
comm->barrier();
os << *getSourceMap();
comm->barrier();
if (myImageID == 0) {
os << endl << endl << "Target Map:" << endl << std::flush;
}
comm->barrier();
os << *getTargetMap();
comm->barrier();
// It's also helpful for debugging to print the Distributor
// object. Epetra_Export::Print() does this, so we can do a
// side-by-side comparison.
if (myImageID == 0) {
os << endl << endl << "Distributor:" << endl << std::flush;
}
comm->barrier();
getDistributor().describe (*(getFancyOStream (rcpFromRef (os))),
Teuchos::VERB_EXTREME);
}
示例5: apply
void
SupportGraph<MatrixType>::
apply (const Tpetra::MultiVector<scalar_type,
local_ordinal_type,
global_ordinal_type,
node_type>& X,
Tpetra::MultiVector<scalar_type,
local_ordinal_type,
global_ordinal_type,
node_type>& Y,
Teuchos::ETransp mode,
scalar_type alpha,
scalar_type beta) const
{
using Teuchos::FancyOStream;
using Teuchos::getFancyOStream;
using Teuchos::RCP;
using Teuchos::rcp;
using Teuchos::rcpFromRef;
using Teuchos::Time;
using Teuchos::TimeMonitor;
typedef scalar_type DomainScalar;
typedef scalar_type RangeScalar;
typedef Tpetra::MultiVector<DomainScalar, local_ordinal_type,
global_ordinal_type, node_type> MV;
RCP<FancyOStream> out = getFancyOStream(rcpFromRef(std::cout));
// Create a timer for this method, if it doesn't exist already.
// TimeMonitor::getNewCounter registers the timer, so that
// TimeMonitor's class methods like summarize() will report the
// total time spent in successful calls to this method.
const std::string timerName ("Ifpack2::SupportGraph::apply");
RCP<Time> timer = TimeMonitor::lookupCounter(timerName);
if (timer.is_null()) {
timer = TimeMonitor::getNewCounter(timerName);
}
{ // Start timing here.
Teuchos::TimeMonitor timeMon (*timer);
TEUCHOS_TEST_FOR_EXCEPTION(
! isComputed(), std::runtime_error,
"Ifpack2::SupportGraph::apply: You must call compute() to compute the "
"incomplete factorization, before calling apply().");
TEUCHOS_TEST_FOR_EXCEPTION(
X.getNumVectors() != Y.getNumVectors(), std::runtime_error,
"Ifpack2::SupportGraph::apply: X and Y must have the same number of "
"columns. X has " << X.getNumVectors() << " columns, but Y has "
<< Y.getNumVectors() << " columns.");
TEUCHOS_TEST_FOR_EXCEPTION(
beta != STS::zero(), std::logic_error,
"Ifpack2::SupportGraph::apply: This method does not currently work when "
"beta != 0.");
// If X and Y are pointing to the same memory location,
// we need to create an auxiliary vector, Xcopy
RCP<const MV> Xcopy;
if (X.getLocalMV().getValues() == Y.getLocalMV().getValues()) {
Xcopy = rcp (new MV(X));
}
else {
Xcopy = rcpFromRef(X);
}
if (alpha != STS::one()) {
Y.scale(alpha);
}
RCP<MV> Ycopy = rcpFromRef(Y);
solver_->setB(Xcopy);
solver_->setX(Ycopy);
solver_->solve ();
} // Stop timing here.
++NumApply_;
// timer->totalElapsedTime() returns the total time over all timer
// calls. Thus, we use = instead of +=.
ApplyTime_ = timer->totalElapsedTime();
}
示例6: main
int
main (int argc, char *argv[])
{
using namespace TrilinosCouplings; // Yes, this means I'm lazy.
using TpetraIntrepidPoissonExample::exactResidualNorm;
using TpetraIntrepidPoissonExample::makeMatrixAndRightHandSide;
using TpetraIntrepidPoissonExample::solveWithBelos;
using TpetraIntrepidPoissonExample::solveWithBelosGPU;
using IntrepidPoissonExample::makeMeshInput;
using IntrepidPoissonExample::parseCommandLineArguments;
using IntrepidPoissonExample::setCommandLineArgumentDefaults;
using IntrepidPoissonExample::setMaterialTensorOffDiagonalValue;
using IntrepidPoissonExample::setUpCommandLineArguments;
using Tpetra::DefaultPlatform;
using Teuchos::Comm;
using Teuchos::outArg;
using Teuchos::ParameterList;
using Teuchos::parameterList;
using Teuchos::RCP;
using Teuchos::rcp;
using Teuchos::rcpFromRef;
using Teuchos::getFancyOStream;
using Teuchos::FancyOStream;
using std::endl;
// Pull in typedefs from the example's namespace.
typedef TpetraIntrepidPoissonExample::ST ST;
#ifdef HAVE_TRILINOSCOUPLINGS_MUELU
typedef TpetraIntrepidPoissonExample::LO LO;
typedef TpetraIntrepidPoissonExample::GO GO;
#endif // HAVE_TRILINOSCOUPLINGS_MUELU
typedef TpetraIntrepidPoissonExample::Node Node;
typedef Teuchos::ScalarTraits<ST> STS;
typedef STS::magnitudeType MT;
typedef Teuchos::ScalarTraits<MT> STM;
typedef TpetraIntrepidPoissonExample::sparse_matrix_type sparse_matrix_type;
typedef TpetraIntrepidPoissonExample::vector_type vector_type;
typedef TpetraIntrepidPoissonExample::operator_type operator_type;
bool success = true;
try {
Teuchos::oblackholestream blackHole;
Teuchos::GlobalMPISession mpiSession (&argc, &argv, &blackHole);
const int myRank = mpiSession.getRank ();
//const int numProcs = mpiSession.getNProc ();
// Get the default communicator and Kokkos Node instance
RCP<const Comm<int> > comm =
DefaultPlatform::getDefaultPlatform ().getComm ();
RCP<Node> node = DefaultPlatform::getDefaultPlatform ().getNode ();
// Did the user specify --help at the command line to print help
// with command-line arguments?
bool printedHelp = false;
// Values of command-line arguments.
int nx, ny, nz;
std::string xmlInputParamsFile;
bool verbose, debug;
int maxNumItersFromCmdLine = -1; // -1 means "read from XML file"
double tolFromCmdLine = -1.0; // -1 means "read from XML file"
std::string solverName = "GMRES";
ST materialTensorOffDiagonalValue = 0.0;
// Set default values of command-line arguments.
setCommandLineArgumentDefaults (nx, ny, nz, xmlInputParamsFile,
solverName, verbose, debug);
// Parse and validate command-line arguments.
Teuchos::CommandLineProcessor cmdp (false, true);
setUpCommandLineArguments (cmdp, nx, ny, nz, xmlInputParamsFile,
solverName, tolFromCmdLine,
maxNumItersFromCmdLine,
verbose, debug);
cmdp.setOption ("materialTensorOffDiagonalValue",
&materialTensorOffDiagonalValue, "Off-diagonal value in "
"the material tensor. This controls the iteration count. "
"Be careful with this if you use CG, since you can easily "
"make the matrix indefinite.");
// Additional command-line arguments for GPU experimentation.
bool gpu = false;
cmdp.setOption ("gpu", "no-gpu", &gpu,
"Run example using GPU node (if supported)");
int ranks_per_node = 1;
cmdp.setOption ("ranks_per_node", &ranks_per_node,
"Number of MPI ranks per node");
int gpu_ranks_per_node = 1;
cmdp.setOption ("gpu_ranks_per_node", &gpu_ranks_per_node,
"Number of MPI ranks per node for GPUs");
int device_offset = 0;
cmdp.setOption ("device_offset", &device_offset,
"Offset for attaching MPI ranks to CUDA devices");
// Additional command-line arguments for dumping the generated
// matrix or its row Map to output files.
//
// FIXME (mfh 09 Apr 2014) Need to port these command-line
// arguments to the Epetra version.
// If matrixFilename is nonempty, dump the matrix to that file
// in MatrixMarket format.
//.........这里部分代码省略.........
示例7: main
int
main (int argc, char *argv[])
{
using Teuchos::ArrayRCP;
using Teuchos::ArrayView;
using Teuchos::Comm;
using Teuchos::CommandLineProcessor;
using Teuchos::FancyOStream;
using Teuchos::getFancyOStream;
using Teuchos::OSTab;
using Teuchos::ptr;
using Teuchos::RCP;
using Teuchos::rcp;
using Teuchos::rcpFromRef;
using std::cout;
using std::endl;
bool success = true; // May be changed by tests
Teuchos::oblackholestream blackHole;
//Teuchos::GlobalMPISession (&argc, &argv, &blackHole);
MPI_Init (&argc, &argv);
//
// Construct communicators, and verify that we are on 4 processors.
//
// Construct a Teuchos Comm object.
RCP<const Comm<int> > teuchosComm = Teuchos::DefaultComm<int>::getComm();
const int numProcs = teuchosComm->getSize();
const int pid = teuchosComm->getRank();
RCP<FancyOStream> pOut =
getFancyOStream (rcpFromRef ((pid == 0) ? std::cout : blackHole));
FancyOStream& out = *pOut;
// Verify that we are on four processors (which manifests the bug).
if (teuchosComm->getSize() != 4) {
out << "This test must be run on four processors. Exiting ..." << endl;
return EXIT_FAILURE;
}
// We also need an Epetra Comm, so that we can compare Tpetra and
// Epetra results.
Epetra_MpiComm epetraComm (MPI_COMM_WORLD);
//
// Default values of command-line options.
//
bool verbose = false;
bool printEpetra = false;
bool printTpetra = false;
CommandLineProcessor cmdp (false,true);
//
// Set command-line options.
//
cmdp.setOption ("verbose", "quiet", &verbose, "Print verbose output.");
// Epetra and Tpetra output will ask the Maps and Import objects to
// print themselves in distributed, maximally verbose fashion. It's
// best to turn on either Epetra or Tpetra, but not both. Then you
// can compare their output side by side.
cmdp.setOption ("printEpetra", "dontPrintEpetra", &printEpetra,
"Print Epetra output (in verbose mode only).");
cmdp.setOption ("printTpetra", "dontPrintTpetra", &printTpetra,
"Print Tpetra output (in verbose mode only).");
// Parse command-line options.
if (cmdp.parse (argc,argv) != CommandLineProcessor::PARSE_SUCCESSFUL) {
out << "End Result: TEST FAILED" << endl;
MPI_Finalize ();
return EXIT_FAILURE;
}
if (verbose) {
out << "Running test on " << numProcs << " process"
<< (numProcs != 1 ? "es" : "") << "." << endl;
}
// The maps for this problem are derived from a 3D structured mesh.
// In this example, the dimensions are 4x4x2 and there are 2
// processors assigned to the first dimension and 2 processors
// assigned to the second dimension, with no parallel decomposition
// along the third dimension. The "owned" arrays represent the
// one-to-one map, with each array representing a 2x2x2 slice. If
// DIMENSIONS == 2, then only the first 4 values will be used,
// representing a 2x2(x1) slice.
int owned0[8] = { 0, 1, 4, 5,16,17,20,21};
int owned1[8] = { 2, 3, 6, 7,18,19,22,23};
int owned2[8] = { 8, 9,12,13,24,25,28,29};
int owned3[8] = {10,11,14,15,26,27,30,31};
// The "overlap" arrays represent the map with communication
// elements, with each array representing a 3x3x2 slice. If
// DIMENSIONS == 2, then only the first 9 values will be used,
// representing a 3x3(x1) slice.
int overlap0[18] = {0,1,2,4, 5, 6, 8, 9,10,16,17,18,20,21,22,24,25,26};
int overlap1[18] = {1,2,3,5, 6, 7, 9,10,11,17,18,19,21,22,23,25,26,27};
int overlap2[18] = {4,5,6,8, 9,10,12,13,14,20,21,22,24,25,26,28,29,30};
int overlap3[18] = {5,6,7,9,10,11,13,14,15,21,22,23,25,26,27,29,30,31};
// Construct the owned and overlap maps for both Epetra and Tpetra.
int* owned;
int* overlap;
//.........这里部分代码省略.........
示例8: main
int
main (int argc, char *argv[])
{
using namespace TrilinosCouplings; // Yes, this means I'm lazy.
using TpetraIntrepidPoissonExample::exactResidualNorm;
using TpetraIntrepidPoissonExample::makeMatrixAndRightHandSide;
using TpetraIntrepidPoissonExample::solveWithBelos;
using TpetraIntrepidPoissonExample::solveWithBelosGPU;
using IntrepidPoissonExample::makeMeshInput;
using IntrepidPoissonExample::setCommandLineArgumentDefaults;
using IntrepidPoissonExample::setUpCommandLineArguments;
using IntrepidPoissonExample::parseCommandLineArguments;
using Tpetra::DefaultPlatform;
using Teuchos::Comm;
using Teuchos::outArg;
using Teuchos::ParameterList;
using Teuchos::parameterList;
using Teuchos::RCP;
using Teuchos::rcp;
using Teuchos::rcpFromRef;
using Teuchos::getFancyOStream;
using Teuchos::FancyOStream;
using std::endl;
// Pull in typedefs from the example's namespace.
typedef TpetraIntrepidPoissonExample::ST ST;
typedef TpetraIntrepidPoissonExample::LO LO;
typedef TpetraIntrepidPoissonExample::GO GO;
typedef TpetraIntrepidPoissonExample::Node Node;
typedef Teuchos::ScalarTraits<ST> STS;
typedef STS::magnitudeType MT;
typedef Teuchos::ScalarTraits<MT> STM;
typedef TpetraIntrepidPoissonExample::sparse_matrix_type sparse_matrix_type;
typedef TpetraIntrepidPoissonExample::vector_type vector_type;
typedef TpetraIntrepidPoissonExample::operator_type operator_type;
bool success = true;
try {
Teuchos::oblackholestream blackHole;
Teuchos::GlobalMPISession mpiSession (&argc, &argv, &blackHole);
const int myRank = mpiSession.getRank ();
//const int numProcs = mpiSession.getNProc ();
// Get the default communicator and Kokkos Node instance
RCP<const Comm<int> > comm =
DefaultPlatform::getDefaultPlatform ().getComm ();
RCP<Node> node = DefaultPlatform::getDefaultPlatform ().getNode ();
// Did the user specify --help at the command line to print help
// with command-line arguments?
bool printedHelp = false;
// Values of command-line arguments.
int nx, ny, nz;
std::string xmlInputParamsFile;
bool verbose, debug;
// Set default values of command-line arguments.
setCommandLineArgumentDefaults (nx, ny, nz, xmlInputParamsFile,
verbose, debug);
// Parse and validate command-line arguments.
Teuchos::CommandLineProcessor cmdp (false, true);
setUpCommandLineArguments (cmdp, nx, ny, nz, xmlInputParamsFile,
verbose, debug);
bool gpu = false;
cmdp.setOption ("gpu", "no-gpu", &gpu,
"Run example using GPU node (if supported)");
int ranks_per_node = 1;
cmdp.setOption("ranks_per_node", &ranks_per_node,
"Number of MPI ranks per node");
int gpu_ranks_per_node = 1;
cmdp.setOption("gpu_ranks_per_node", &gpu_ranks_per_node,
"Number of MPI ranks per node for GPUs");
int device_offset = 0;
cmdp.setOption("device_offset", &device_offset,
"Offset for attaching MPI ranks to CUDA devices");
parseCommandLineArguments (cmdp, printedHelp, argc, argv, nx, ny, nz,
xmlInputParamsFile, verbose, debug);
if (printedHelp) {
// The user specified --help at the command line to print help
// with command-line arguments. We printed help already, so quit
// with a happy return code.
return EXIT_SUCCESS;
}
// Both streams only print on MPI Rank 0. "out" only prints if the
// user specified --verbose.
RCP<FancyOStream> out =
getFancyOStream (rcpFromRef ((myRank == 0 && verbose) ? std::cout : blackHole));
RCP<FancyOStream> err =
getFancyOStream (rcpFromRef ((myRank == 0 && debug) ? std::cerr : blackHole));
#ifdef HAVE_MPI
*out << "PARALLEL executable" << endl;
#else
*out << "SERIAL executable" << endl;
#endif
/**********************************************************************************/
/********************************** GET XML INPUTS ********************************/
//.........这里部分代码省略.........