"vscode:/vscode.git/clone" did not exist on "4a2589f010382064822e0ef8ea2735a86f4a6864"
Commit 55e5a777 authored by shunbo's avatar shunbo
Browse files

initial commit

parents
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011 OpenFOAM Foundation
Copyright (C) 2017-2019 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Application
Description
\*---------------------------------------------------------------------------*/
#include "OSspecific.H"
#include "IOstreams.H"
#include "DLList.H"
#include "List.H"
#include "FlatOutput.H"
#include "ListOps.H"
using namespace Foam;
template<class T>
void printAddress(const UList<T>& list)
{
Info<< "list addr: " << name(&list)
<< " data addr: " << name(list.cdata()) << nl;
}
template<class T>
void printAddresses(const DLList<List<T>>& sll)
{
for (const auto& elem : sll)
{
printAddress(elem);
}
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:
int main(int argc, char *argv[])
{
DLList<scalar> myList{2.1, 3.4};
myList = {2.1, 3.4, 4.3};
for (int i = 0; i<10; i++)
{
myList.append(1.3*i);
}
myList.append(100.3);
myList.append(500.3);
Info<< "DLList<scalar>" << nl;
Info<< nl << "flat-output: " << flatOutput(myList) << nl;
Info<< nl << "range-for:" << nl;
for (const auto& val : myList)
{
Info<< " " << val << nl;
}
Info<< nl << "const_iterator:" << nl;
forAllConstIters(myList, iter)
{
Info<< " " << *iter << endl;
}
// Test bi-directional movement
{
const label n2 = myList.size()/2;
Info<< nl << "test movement through " << flatOutput(myList) << nl;
DLList<scalar>::const_iterator citer = myList.begin();
for (label i=0; i<n2; ++i)
{
Info<< " forward " << i << " " << *citer << nl;
++citer;
}
for (label i=0; i<n2; ++i)
{
Info<< " backward " << i << " " << *citer << nl;
--citer;
}
// Verify - does not compile since it uses a delete method (good!)
DLList<scalar>::iterator iter = myList.begin();
++iter;
++iter;
++iter;
Info<<" now with " << *iter << nl;
myList.remove(iter);
Info<<" after remove " << *iter << nl;
++iter;
Info<<" after incr " << *iter << nl;
--iter;
--iter;
Info<<" after 2x decr " << *iter << nl;
}
Info<< nl << "const_reverse_iterator:" << nl;
forAllConstReverseIters(myList, iter)
{
Info<< " " << *iter << endl;
}
Info<< nl << "Remove elements:" << nl;
forAllIters(myList, iter)
{
Info<< " remove " << *iter;
myList.remove(iter);
Info<< " => " << flatOutput(myList) << nl;
}
myList.append(500.3);
myList.append(200.3);
myList.append(100.3);
Info<< nl << "Testing swapUp and swapDown:" << nl;
Info<< " => " << flatOutput(myList) << nl;
{
myList.swapUp(myList.DLListBase::first());
myList.swapUp(myList.DLListBase::last());
Info<< nl << "swapUp => " << flatOutput(myList) << nl;
}
{
myList.swapDown(myList.DLListBase::first());
myList.swapDown(myList.DLListBase::last());
Info<< nl << "swapDown => " << flatOutput(myList) << nl;
}
Info<< nl << "Transfer: " << nl;
Info<< "original: " << flatOutput(myList) << endl;
DLList<scalar> newList;
newList.transfer(myList);
Info<< nl
<< "source: " << flatOutput(myList) << nl
<< "target: " << flatOutput(newList) << nl;
Info<< nl << "Move Construct: " << nl;
DLList<scalar> list2(std::move(newList));
Info<< nl
<< "in : " << flatOutput(newList) << nl
<< "out: " << flatOutput(list2) << nl;
// Move back
Info<< nl << "Move Assignment: " << nl;
newList = std::move(list2);
Info<< nl
<< "in : " << flatOutput(newList) << nl
<< "out: " << flatOutput(list2) << nl;
// Try delete data recovery
{
DLList<List<label>> labList;
for (int i = 0; i<5; i++)
{
labList.append(identity(6));
}
Info<< nl
<< "DLList<labelList> : " << labList << nl;
printAddresses(labList);
auto elem = labList.removeHead();
Info<< " removed head" << nl;
printAddress(elem);
elem = labList.removeHead();
Info<< " removed head" << nl;
printAddress(elem);
List<label> content1 = identity(10);
Info<< nl
<< " move append ";
printAddress(content1);
labList.append(std::move(content1));
Info<< " content " << flatOutput(content1) << nl
<< " list" << labList << nl;
printAddresses(labList);
// labList.append(content1);
}
Info<< nl << "Done." << endl;
return 0;
}
// ************************************************************************* //
Test-DiagTensor.C
EXE = $(FOAM_USER_APPBIN)/Test-DiagTensor
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2020 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Application
Test-DiagTensor
Description
Tests for \c DiagTensor constructors, member functions and operators
using \c floatScalar, \c doubleScalar, and \c complex base types.
Cross-checks were obtained from 'NumPy 1.15.1' and 'SciPy 1.1.0' if no
theoretical cross-check exists (like eigendecomposition relations), and
were hard-coded for elementwise comparisons.
For \c complex base type, the cross-checks do only involve zero imag part.
\*---------------------------------------------------------------------------*/
#include "Tensor.H"
#include "SymmTensor.H"
#include "SphericalTensor.H"
#include "DiagTensor.H"
#include "scalar.H"
#include "complex.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Total number of unit tests
unsigned nTest_ = 0;
// Total number of failed unit tests
unsigned nFail_ = 0;
// Compare two floating point types, and print output.
// Do ++nFail_ if values of two objects are not equal within a given tolerance.
// The function is converted from PEP-485.
template<class Type>
typename std::enable_if
<
std::is_same<floatScalar, Type>::value ||
std::is_same<doubleScalar, Type>::value ||
std::is_same<complex, Type>::value,
void
>::type cmp
(
const word& msg,
const Type& x,
const Type& y,
const scalar relTol = 1e-8, //<! are values the same within 8 decimals
const scalar absTol = 0 //<! useful for cmps near zero
)
{
Info<< msg << x << endl;
unsigned nFail = 0;
if (max(absTol, relTol*max(mag(x), mag(y))) < mag(x - y))
{
++nFail;
}
if (nFail)
{
Info<< nl
<< " #### Fail in " << nFail << " comps ####" << nl << endl;
++nFail_;
}
++nTest_;
}
// Compare two containers elementwise, and print output.
// Do ++nFail_ if two components are not equal within a given tolerance.
// The function is converted from PEP-485
template<class Type>
typename std::enable_if
<
!std::is_same<floatScalar, Type>::value &&
!std::is_same<doubleScalar, Type>::value &&
!std::is_same<complex, Type>::value,
void
>::type cmp
(
const word& msg,
const Type& x,
const Type& y,
const scalar relTol = 1e-8,
const scalar absTol = 0
)
{
Info<< msg << x << endl;
unsigned nFail = 0;
for (label i = 0; i < pTraits<Type>::nComponents; ++i)
{
if (max(absTol, relTol*max(mag(x[i]), mag(y[i]))) < mag(x[i] - y[i]))
{
++nFail;
}
}
if (nFail)
{
Info<< nl
<< " #### Fail in " << nFail << " comps ####" << nl << endl;
++nFail_;
}
++nTest_;
}
// Create each constructor of DiagTensor<Type>, and print output
template<class Type>
void test_constructors(Type)
{
{
Info<< "# Construct initialized to zero:" << nl;
const DiagTensor<Type> dT(Zero);
Info<< dT << endl;
}
{
Info<< "# Construct given VectorSpace of the same rank:" << nl;
const VectorSpace<DiagTensor<Type>, Type, 3> V(Zero);
const DiagTensor<Type> dT(V);
Info<< dT << endl;
}
{
Info<< "# Construct given the three components:" << nl;
const DiagTensor<Type> dT
(
Type(1),
Type(5),
Type(-9)
);
Info<< dT << endl;
}
{
Info<< "# Copy construct:" << nl;
const DiagTensor<Type> dT(Zero);
const DiagTensor<Type> copydT(dT);
Info<< dT << tab << copydT << endl;
}
}
// Execute each member function of DiagTensor<Type>, and print output
template<class Type>
void test_member_funcs(Type)
{
DiagTensor<Type> dT(Type(1), Type(5), Type(-9));
const DiagTensor<Type> cdT(Type(-9), Type(5), Type(1));
Info<< "# Operand: " << nl
<< " DiagTensor = " << dT << endl;
{
Info<< "# Component access:" << nl;
DiagTensor<Type> cpdT(dT.xx(), dT.yy(), dT.zz());
cmp(" 'DiagTensor' access:", dT, cpdT);
const DiagTensor<Type> cpcdT(cdT.xx(), cdT.yy(), cdT.zz());
cmp(" 'const DiagTensor' access:", cdT, cpcdT);
}
}
// Execute each global function of DiagTensor<Type>, and print output
template<class Type>
void test_global_funcs(Type)
{
const Tensor<Type> T
(
Type(-1), Type(2), Type(-3),
Type(4), Type(5), Type(-6),
Type(7), Type(8), Type(-9)
);
const SymmTensor<Type> sT
(
Type(-1), Type(2), Type(-3),
Type(5), Type(-6),
Type(-9)
);
const DiagTensor<Type> dT(Type(1), Type(5), Type(-9));
Info<< "# Operands: " << nl
<< " Tensor = " << T << nl
<< " SymmTensor = " << sT << nl
<< " DiagTensor = " << dT << endl;
cmp(" Trace = ", tr(dT), Type(-3));
cmp(" Spherical part = ", sph(dT), SphericalTensor<Type>(tr(dT)/Type(3)));
cmp(" Determinant = ", det(dT), Type(-44.99999999999999));
cmp
(
" Inverse = ",
inv(dT),
DiagTensor<Type>(Type(1), Type(0.2), Type(-0.11111111))
);
cmp
(
" Diagonal of Tensor = ",
diag(T),
DiagTensor<Type>(Type(-1), Type(5), Type(-9))
);
cmp
(
" Diagonal of SymmTensor = ",
diag(sT),
DiagTensor<Type>(Type(-1), Type(5), Type(-9))
);
}
// Execute each global operator of DiagTensor<Type>, and print output
template<class Type>
void test_global_opers(Type)
{
const Tensor<Type> T
(
Type(-1), Type(2), Type(-3),
Type(4), Type(5), Type(-6),
Type(7), Type(8), Type(-9)
);
const SymmTensor<Type> sT
(
Type(-1), Type(2), Type(-3),
Type(5), Type(-6),
Type(-9)
);
const DiagTensor<Type> dT(Type(1), Type(5), Type(-9));
const SphericalTensor<Type> spT(Type(1));
const Vector<Type> v(Type(3), Type(2), Type(1));
const Type x(4);
Info<< "# Operands:" << nl
<< " Tensor = " << T << nl
<< " SymmTensor = " << sT << nl
<< " DiagTensor = " << dT << nl
<< " SphericalTensor = " << spT << nl
<< " Vector = " << v << nl
<< " Type = " << x << endl;
cmp
(
" Sum of DiagTensor-Tensor = ",
(dT + T),
Tensor<Type>
(
Type(0), Type(2), Type(-3),
Type(4), Type(10), Type(-6),
Type(7), Type(8), Type(-18)
)
);
cmp
(
" Sum of Tensor-DiagTensor = ",
(T + dT),
Tensor<Type>
(
Type(0), Type(2), Type(-3),
Type(4), Type(10), Type(-6),
Type(7), Type(8), Type(-18)
)
);
cmp
(
" Subtract Tensor from DiagTensor = ",
(dT - T),
Tensor<Type>
(
Type(2), Type(-2), Type(3),
Type(-4), Type(0), Type(6),
Type(-7), Type(-8), Type(0)
)
);
cmp
(
" Subtract DiagTensor from Tensor = ",
(T - dT),
Tensor<Type>
(
Type(-2), Type(2), Type(-3),
Type(4), Type(0), Type(-6),
Type(7), Type(8), Type(0)
)
);
cmp
(
" Division of Type by DiagTensor = ",
(x/dT),
DiagTensor<Type>(Type(4), Type(0.8), Type(-0.44444444))
);
cmp
(
" Division of DiagTensor by Type = ",
(dT/x),
DiagTensor<Type>(Type(0.25), Type(1.25), Type(-2.25))
);
cmp
(
" Division of Vector by DiagTensor = ",
(v/dT),
Vector<Type>(Type(3), Type(0.4), Type(-0.11111111))
);
cmp
(
" Inner-product of DiagTensor-DiagTensor = ",
(dT & dT),
DiagTensor<Type>(Type(1), Type(25), Type(81))
);
cmp
(
" Inner-product of DiagTensor-Tensor = ",
(dT & T),
Tensor<Type>
(
Type(-1), Type(2), Type(-3),
Type(20), Type(25), Type(-30),
Type(-63), Type(-72), Type(81)
)
);
cmp
(
" Inner-product of Tensor-DiagTensor = ",
(T & dT),
Tensor<Type>
(
Type(-1), Type(10), Type(27),
Type(4), Type(25), Type(54),
Type(7), Type(40), Type(81)
)
);
cmp
(
" Inner-product of DiagTensor-Vector = ",
(dT & v),
Vector<Type>(Type(3), Type(10), Type(-9))
);
cmp
(
" Inner-product of Vector-DiagTensor = ",
(v & dT),
Vector<Type>(Type(3), Type(10), Type(-9))
);
}
// Do compile-time recursion over the given types
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
run_tests(const std::tuple<Tp...>& types, const List<word>& typeID){}
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
run_tests(const std::tuple<Tp...>& types, const List<word>& typeID)
{
Info<< nl << " ## Test constructors: "<< typeID[I] <<" ##" << nl;
test_constructors(std::get<I>(types));
Info<< nl << " ## Test member functions: "<< typeID[I] <<" ##" << nl;
test_member_funcs(std::get<I>(types));
Info<< nl << " ## Test global functions: "<< typeID[I] << " ##" << nl;
test_global_funcs(std::get<I>(types));
Info<< nl << " ## Test global operators: "<< typeID[I] <<" ##" << nl;
test_global_opers(std::get<I>(types));
run_tests<I + 1, Tp...>(types, typeID);
}
// * * * * * * * * * * * * * * * Main Program * * * * * * * * * * * * * * * //
int main()
{
const std::tuple<floatScalar, doubleScalar, complex> types
(
std::make_tuple(Zero, Zero, Zero)
);
const List<word> typeID
({
"DiagTensor<floatScalar>",
"DiagTensor<doubleScalar>",
"DiagTensor<complex>"
});
run_tests(types, typeID);
if (nFail_)
{
Info<< nl << " #### "
<< "Failed in " << nFail_ << " tests "
<< "out of total " << nTest_ << " tests "
<< "####\n" << endl;
return 1;
}
Info<< nl << " #### Passed all " << nTest_ <<" tests ####\n" << endl;
return 0;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
Test-Dictionary.C
EXE = $(FOAM_USER_APPBIN)/Test-Dictionary
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011 OpenFOAM Foundation
Copyright (C) 2020 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Application
Description
\*---------------------------------------------------------------------------*/
#include "OSspecific.H"
#include "scalar.H"
#include "IOstreams.H"
#include "Dictionary.H"
#include "PtrDictionary.H"
using namespace Foam;
class ent
:
public Dictionary<ent>::link
{
word keyword_;
int i_;
public:
ent(const word& keyword, int i)
:
keyword_(keyword),
i_(i)
{}
const word& keyword() const
{
return keyword_;
}
friend Ostream& operator<<(Ostream& os, const ent& e)
{
os << e.keyword_ << ' ' << e.i_ << endl;
return os;
}
};
class Scalar
{
scalar data_;
public:
Scalar()
:
data_(0)
{}
Scalar(scalar val)
:
data_(val)
{}
~Scalar()
{
Info<<"delete Scalar: " << data_ << endl;
}
friend Ostream& operator<<(Ostream& os, const Scalar& val)
{
os << val.data_;
return os;
}
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:
int main(int argc, char *argv[])
{
Dictionary<ent>* dictPtr = new Dictionary<ent>;
Dictionary<ent>& dict = *dictPtr;
for (int i = 0; i<10; i++)
{
ent* ePtr = new ent(word("ent") + name(i), i);
dict.append(ePtr->keyword(), ePtr);
dict.swapUp(ePtr);
}
Info<< dict << endl;
dict.swapDown(dict.first());
forAllConstIter(Dictionary<ent>, dict, iter)
{
Info<< "element : " << *iter;
}
Info<< "keys: " << dict.toc() << endl;
delete dictPtr;
Dictionary<ent> dict2;
for (int i = 0; i<10; i++)
{
ent* ePtr = new ent(word("ent") + name(i), i);
dict2.append(ePtr->keyword(), ePtr);
dict2.swapUp(ePtr);
}
Info<< "dict:\n" << dict2 << endl;
Info<< nl << "Testing transfer: " << nl << endl;
Info<< "original: " << dict2 << endl;
Dictionary<ent> newDict;
newDict.transfer(dict2);
Info<< nl << "source: " << dict2 << nl
<< "keys: " << dict2.toc() << nl
<< "target: " << newDict << nl
<< "keys: " << newDict.toc() << endl;
PtrDictionary<Scalar> scalarDict;
for (int i = 0; i<10; i++)
{
word key("ent" + name(i));
scalarDict.insert(key, new Scalar(1.3*i));
}
Info<< nl << "scalarDict1: " << endl;
forAllConstIter(PtrDictionary<Scalar>, scalarDict, iter)
{
Info<< " = " << iter() << endl;
}
PtrDictionary<Scalar> scalarDict2;
for (int i = 8; i<15; i++)
{
word key("ent" + name(i));
scalarDict2.insert(key, new Scalar(1.3*i));
}
Info<< nl << "scalarDict2: " << endl;
forAllConstIter(PtrDictionary<Scalar>, scalarDict2, iter)
{
std::cout<< "iter: " << typeid(*iter).name() << '\n';
Info<< "elem = " << *iter << endl;
}
// FIXME: the deduction seems to be different here.
// - returns pointer (as perhaps actually expected) not the
// underlying value.
forAllConstIters(scalarDict2, iter)
{
std::cout<< "iter: " << typeid(*iter).name() << '\n';
// Info<< "elem = " << *(*iter) << endl;
}
std::cout<< "iter type: "
<< typeid(stdFoam::begin(scalarDict2)).name() << '\n';
scalarDict.transfer(scalarDict2);
const Scalar* p = scalarDict.cfind("ent8");
// This does not (yet) work
// Scalar* q = scalarDict.remove("ent10");
if (p)
{
Info<< "found: " << *p << endl;
}
else
{
Info<< "no p: " << endl;
}
scalarDict.clear();
// Info<< " = " << *iter << endl;
Info<< nl << "Done." << endl;
return 0;
}
// ************************************************************************* //
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "DirLister.H"
#include <dirent.h>
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
static const Foam::word extgz("gz");
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
bool Foam::DirLister::const_iterator::open(const fileName& dir)
{
if (!dir.empty())
{
dirptr_ = ::opendir(dir.c_str());
}
return dirptr_;
}
void Foam::DirLister::const_iterator::close()
{
if (dirptr_)
{
::closedir(dirptr_);
dirptr_ = nullptr;
}
}
Foam::word Foam::DirLister::next(DIR* dirPtr) const
{
// Read next entry in the directory
struct dirent *list;
while (dirPtr && (list = ::readdir(dirPtr)) != nullptr)
{
const std::string item(list->d_name);
// Ignore files/directories beginning with "."
// These are the ".", ".." directories and any hidden files/dirs
if (item.empty() || item[0] == '.')
{
continue;
}
// Validate filename without spaces, quotes, etc in the name.
// No duplicate slashes to strip - dirent will not have them anyhow.
word name(fileName::validate(item));
if (name != item)
{
// ++nFailed_;
continue;
}
bool ok = false;
fileName::Type fType = fileName::UNDEFINED;
if
(
(requestedType_ == fileName::DIRECTORY)
|| (requestedType_ == fileName::FILE && !fileName::isBackup(name))
)
{
fType = (dirName_/name).type(followLink_);
// A DIRECTORY or FILE was request, so only accept the same type
ok = (requestedType_ == fType);
}
else if (requestedType_ == fileName::UNDEFINED)
{
fType = (dirName_/name).type(followLink_);
// An UNDEFINED type was requested, so accept DIRECTORY or FILE
ok =
(
(fType == fileName::DIRECTORY)
|| (fType == fileName::FILE && !fileName::isBackup(name))
);
}
if (ok)
{
if (fType == fileName::FILE && stripgz_ && name.hasExt(extgz))
{
name = name.lessExt();
}
if (!name.empty() && accept(name))
{
return name;
}
}
}
return word();
}
// * * * * * * * * * * * * * * * Const Iterator * * * * * * * * * * * * * * //
bool Foam::DirLister::const_iterator::next()
{
if (lister_ && dirptr_)
{
name_ = lister_->next(dirptr_);
if (name_.empty())
{
close();
}
}
return dirptr_;
}
// ************************************************************************* //
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::DirLister
Description
A class for listing directory contents, or obtaining a list of
directory contents.
For many situations, the iterator-based access is the most convenient
since it allows use of a range-for without any intermediate variables
or allocations.
List all directories or files:
\code
for (const word& item : DirLister("."))
{
Info<< "dir or file: " << item << nl;
}
\endcode
List files or directories only:
\code
for (const word& item : DirLister(".").files())
{
Info<< "file: " << item << nl;
}
for (const word& item : DirLister(".").dirs())
{
Info<< "dir: " << item << nl;
}
\endcode
These can be combined with a unary predicate to select only specific
items:
\code
{
wordRes matchProcs
{
wordRe("processors"),
wordRe("processor\\d+", wordRe::REGEX)
};
for
(
const word& item
: DirLister::dirs(".").where(matchProcs)
)
{
Info<< "processor dir: " << item << nl;
}
}
\endcode
The unary predicate can similarly be used with the list or sorted
methods:
\code
{
wordRes matchProcs
{
wordRe("processor[0-9][0-9]*", wordRe::REGEX),
wordRe("processors")
};
fileNameList procDirs
(
DirLister::dirs(".").sorted<fileName>(matchProcs)
);
}
\endcode
See Also
Foam::readDir
SourceFiles
DirListerI.H
DirLister.C
DirListerTemplates.C
\*---------------------------------------------------------------------------*/
#ifndef DirLister_H
#define DirLister_H
#include "fileNameList.H"
#include "wordList.H"
#include <dirent.h>
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class DirLister Declaration
\*---------------------------------------------------------------------------*/
class DirLister
{
// Private data
fileName dirName_;
fileName::Type requestedType_;
bool followLink_;
bool stripgz_;
protected:
// Protected Member Functions
//- Placeholder for additional selection filters
virtual inline bool accept(const word& name) const;
//- Traverse to next entry
word next(DIR* dirPtr) const;
public:
//- Specialization for filtering with a predicate
template<class UnaryPredicate> class Filtered;
// Constructors
//- Construct from components - list files and directories
inline DirLister
(
const fileName& directory,
const fileName::Type = fileName::UNDEFINED,
const bool filtergz = true,
const bool followLink = true
);
//- Convenience constructor for listing directories
inline static DirLister dirs
(
const fileName& directory,
const bool followLink = true
);
//- Convenience constructor for listing files
inline static DirLister files
(
const fileName& directory,
const bool filtergz = true,
const bool followLink = true
);
//- Convenience for constructing a filtered iterator
template<class UnaryPredicate>
inline Filtered<UnaryPredicate> where
(
const UnaryPredicate& pred,
const bool prune = false
) const;
//- Destructor
virtual ~DirLister() = default;
// Member Functions
//- Return the directory name
inline const fileName& dirName() const;
//- Return a complete list of names
template<class StringType=Foam::word>
List<StringType> list() const;
//- Return complete list of names
template<class StringType=Foam::word, class UnaryPredicate>
List<StringType> list
(
const UnaryPredicate& pred,
const bool prune = false
) const;
//- Return a complete list of names, sorted in natural order
template<class StringType=Foam::word>
List<StringType> sorted() const;
//- Return complete list of names, sorted in natural order
template<class StringType=Foam::word, class UnaryPredicate>
List<StringType> sorted
(
const UnaryPredicate& pred,
const bool prune = false
) const;
// Iterators
//- Const iterator for traversing directory contents
class const_iterator
{
const DirLister* lister_;
DIR* dirptr_;
word name_;
//- Open directory and set dirptr_ (nullptr on failure)
bool open(const fileName& dir);
//- Close dirptr_
void close();
bool next();
public:
//- Construct end iterator
inline const_iterator();
//- Construct begin iterator
inline const_iterator(const DirLister* lister);
//- Destructor
inline ~const_iterator();
inline const_iterator& operator++();
inline const word& operator*() const;
inline bool operator==(const const_iterator& iter) const;
inline bool operator!=(const const_iterator& iter) const;
};
inline const_iterator cbegin() const;
inline const_iterator cend() const;
inline const_iterator begin() const;
inline const_iterator end() const;
};
//- A DirLister specialization for filtering with a predicate
template<class UnaryPredicate>
class DirLister::Filtered
:
public DirLister
{
const UnaryPredicate& pred_;
bool prune_;
protected:
friend class DirLister;
virtual inline bool accept(const word& name) const;
//- Constructor
inline Filtered
(
const DirLister& lister,
const UnaryPredicate& pred,
const bool prune = false
);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "DirListerI.H"
#ifdef NoRepository
#include "DirListerTemplates.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
inline bool Foam::DirLister::accept(const word& name) const
{
return true;
}
template<class UnaryPredicate>
inline bool Foam::DirLister::Filtered<UnaryPredicate>::accept
(
const word& name
) const
{
return (pred_(name) ? !prune_ : prune_);
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
inline Foam::DirLister::DirLister
(
const fileName& directory,
const fileName::Type requestedType,
const bool filtergz,
const bool followLink
)
:
dirName_(directory),
requestedType_(requestedType),
followLink_(followLink),
stripgz_(filtergz)
{}
inline Foam::DirLister Foam::DirLister::dirs
(
const fileName& directory,
const bool followLink
)
{
return DirLister(directory, fileName::DIRECTORY, false, followLink);
}
inline Foam::DirLister Foam::DirLister::files
(
const fileName& directory,
const bool filtergz,
const bool followLink
)
{
return DirLister(directory, fileName::FILE, filtergz, followLink);
}
template<class UnaryPredicate>
inline Foam::DirLister::Filtered<UnaryPredicate>::Filtered
(
const Foam::DirLister& lister,
const UnaryPredicate& pred,
const bool prune
)
:
DirLister(lister),
pred_(pred),
prune_(prune)
{}
template<class UnaryPredicate>
inline Foam::DirLister::Filtered<UnaryPredicate>
Foam::DirLister::where(const UnaryPredicate& pred, const bool prune) const
{
return DirLister::Filtered<UnaryPredicate>(*this, pred, prune);
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
inline const Foam::fileName& Foam::DirLister::dirName() const
{
return dirName_;
}
// * * * * * * * * * * * * * * * Const Iterator * * * * * * * * * * * * * * //
inline Foam::DirLister::const_iterator::const_iterator()
:
lister_(nullptr),
dirptr_(nullptr),
name_()
{}
inline Foam::DirLister::const_iterator::const_iterator
(
const Foam::DirLister* lister
)
:
lister_(lister),
dirptr_(nullptr),
name_()
{
if (lister_ && open(lister_->dirName()))
{
next(); // increment to first entry
}
}
inline Foam::DirLister::const_iterator::~const_iterator()
{
close();
}
inline const Foam::word& Foam::DirLister::const_iterator::operator*() const
{
return name_;
}
Foam::DirLister::const_iterator&
Foam::DirLister::const_iterator::operator++()
{
next();
return *this;
}
inline bool Foam::DirLister::const_iterator::operator==
(
const const_iterator& iter
)
const
{
// Directory entries are unique, so just compare the names
return (name_ == iter.name_);
}
inline bool Foam::DirLister::const_iterator::operator!=
(
const const_iterator& iter
)
const
{
return name_ != iter.name_;
}
inline Foam::DirLister::const_iterator Foam::DirLister::cbegin() const
{
return const_iterator(this);
}
inline Foam::DirLister::const_iterator Foam::DirLister::cend() const
{
return const_iterator();
}
inline Foam::DirLister::const_iterator Foam::DirLister::begin() const
{
return cbegin();
}
inline Foam::DirLister::const_iterator Foam::DirLister::end() const
{
return cend();
}
// ************************************************************************* //
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "predicates.H"
#include "stringOps.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
template<class StringType, class UnaryPredicate>
Foam::List<StringType> Foam::DirLister::list
(
const UnaryPredicate& pred,
const bool prune
) const
{
// Initial list size and resizing increment
static const int incr = 100;
List<StringType> lst(incr);
label nItems = 0;
for (const auto& item: *this)
{
if (pred(item) ? !prune : prune)
{
if (nItems >= lst.size())
{
lst.setSize(lst.size() + incr);
}
lst[nItems++] = item;
}
}
lst.setSize(nItems);
return lst;
}
template<class StringType>
Foam::List<StringType> Foam::DirLister::list() const
{
return list<StringType>(predicates::always());
}
template<class StringType, class UnaryPredicate>
Foam::List<StringType> Foam::DirLister::sorted
(
const UnaryPredicate& pred,
const bool prune
) const
{
List<StringType> lst(list<StringType>(pred, prune));
sort(lst, stringOps::natural_sort());
return lst;
}
template<class StringType>
Foam::List<StringType> Foam::DirLister::sorted() const
{
return sorted<StringType>(predicates::always());
}
// ************************************************************************* //
Test-DirLister.C
DirLister.C
EXE = $(FOAM_USER_APPBIN)/Test-DirLister
/* EXE_INC = -I$(LIB_SRC)/finiteVolume/lnInclude */
/* EXE_LIBS = -lfiniteVolume */
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Description
Test functionality of DirLister
\*---------------------------------------------------------------------------*/
#include "DirLister.H"
#include "fileNameList.H"
#include "wordRes.H"
#include "predicates.H"
#include "FlatOutput.H"
#include "error.H"
#include "stringOps.H"
#include "scalar.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:
int main(int argc, char *argv[])
{
{
Info<< nl
<< "List items" << nl
<< "~~~~~~~~~~" << nl;
for (const word& item : DirLister("."))
{
Info<< " " << item << nl;
}
}
{
Info<< nl
<< "List files" << nl
<< "~~~~~~~~~~" << nl;
for (const word& item : DirLister::files("."))
{
Info<< " " << item << nl;
}
}
{
Info<< nl
<< "List dirs" << nl
<< "~~~~~~~~~~" << nl;
for (const word& item : DirLister::dirs("."))
{
Info<< " " << item << nl;
}
}
{
Info<< nl
<< "List files - filtered" << nl
<< "~~~~~~~~~~" << nl;
for
(
const word& item
: DirLister::files(".").where
(
[](const word& val){ return val.starts_with('T'); }
)
)
{
Info<< " " << item << nl;
}
}
{
Info<< nl
<< "List dirs - filtered" << nl
<< "~~~~~~~~~~" << nl;
for
(
const word& item
: DirLister::dirs(".").where(regExp("Ma.*"))
)
{
Info<< " " << item << nl;
}
}
{
Info<< nl
<< "List dirs - filtered" << nl
<< "~~~~~~~~~~" << nl;
for
(
const word& item
: DirLister::dirs(".").where(predicates::always())
)
{
Info<< " " << item << nl;
}
}
{
Info<< nl
<< "List items" << nl
<< "~~~~~~~~~~" << nl
<< DirLister(".").list<fileName>() << nl;
}
{
Info<< nl
<< "List files - filtered" << nl
<< "~~~~~~~~~~" << nl
<< DirLister(".").list<fileName>
(
[](const word& val){ return val.starts_with('D'); },
false
)
<< nl;
}
{
Info<< nl
<< "List files - filtered" << nl
<< "~~~~~~~~~~" << nl;
wordRes relist
({
wordRe("processors"),
wordRe("processor[0-9][0-9]*", wordRe::REGEX)
});
Info<<"matcher: " << flatOutput(relist) << endl;
for (const word& item : DirLister::dirs(".").where(relist))
{
Info<< "=> " << item << nl;
}
Info<< "dirList: "
<< flatOutput
(
DirLister::dirs(".").sorted<fileName>(relist)
) << nl;
}
{
Info<< nl
<< "List time dirs" << nl
<< "~~~~~~~~~~" << nl;
for
(
const word& item
: DirLister::dirs(".").where
(
[](const word& val)
{
scalar s;
return readScalar(val, s) || val == "constant";
}
)
)
{
Info<< "=> " << item << nl;
}
}
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //
Test-Distribution.C
EXE = $(FOAM_USER_APPBIN)/Test-DistributionTest
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd |
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Application
Test-Distribution
Description
Test the Distribution class
Plot normal distribution test in gnuplot using:
\verbatim
normalDistribution(mean, sigma, x) = \
sqrt(1.0/(2.0*pi*sigma**2))*exp(-(x - mean)**2.0/(2.0*sigma**2))
plot normalDistribution(8.5, 2.5, x), "Distribution_scalar_test_x" w p
\endverbatim
\*---------------------------------------------------------------------------*/
#include "vector.H"
#include "labelVector.H"
#include "tensor.H"
#include "Distribution.H"
#include "Random.H"
#include "dimensionedTypes.H"
#include "argList.H"
#include "PstreamReduceOps.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
using namespace Foam;
int main(int argc, char *argv[])
{
#include "setRootCase.H"
Random R(918273);
{
// scalar
label randomDistributionTestSize = 50000000;
Distribution<scalar> dS(scalar(5e-2));
Info<< nl << "Distribution<scalar>" << nl
<< "Sampling "
<< randomDistributionTestSize
<< " times from GaussNormal distribution."
<< endl;
for (label i = 0; i < randomDistributionTestSize; i++)
{
dS.add(2.5*R.GaussNormal<scalar>() + 8.5);
}
Info<< "Mean " << dS.mean() << nl
<< "Median " << dS.median()
<< endl;
dS.write("Distribution_scalar_test_1");
Distribution<scalar> dS2(scalar(1e-2));
Info<< nl << "Distribution<scalar>" << nl
<< "Sampling "
<< randomDistributionTestSize
<< " times from GaussNormal distribution."
<< endl;
for (label i = 0; i < randomDistributionTestSize; i++)
{
dS2.add(1.5*R.GaussNormal<scalar>() -6.0);
}
Info<< "Mean " << dS2.mean() << nl
<< "Median " << dS2.median()
<< endl;
dS2.write("Distribution_scalar_test_2");
Info<< nl << "Adding previous two Distribution<scalar>" << endl;
dS = dS + dS2;
dS.write("Distribution_scalar_test_1+2");
}
if (Pstream::parRun())
{
// scalar in parallel
label randomDistributionTestSize = 100000000;
Distribution<scalar> dS(scalar(1e-1));
Pout<< "Distribution<scalar>" << nl
<< "Sampling "
<< randomDistributionTestSize
<< " times from uniform distribution."
<< endl;
for (label i = 0; i < randomDistributionTestSize; i++)
{
dS.add(R.sample01<scalar>() + 10*Pstream::myProcNo());
}
Pout<< "Mean " << dS.mean() << nl
<< "Median " << dS.median()
<< endl;
reduce(dS, sumOp<Distribution<scalar>>());
if (Pstream::master())
{
Info<< "Reducing parallel Distribution<scalar>" << nl
<< "Mean " << dS.mean() << nl
<< "Median " << dS.median()
<< endl;
dS.write("Distribution_scalar_test_parallel_reduced");
}
}
{
// vector
Distribution<vector> dV(vector(0.1, 0.05, 0.15));
label randomDistributionTestSize = 1000000;
Info<< nl << "Distribution<vector>" << nl
<< "Sampling "
<< randomDistributionTestSize
<< " times from uniform and GaussNormal distribution."
<< endl;
for (label i = 0; i < randomDistributionTestSize; i++)
{
dV.add(R.sample01<vector>());
// Adding separate GaussNormal components with component
// weights
dV.add
(
vector
(
R.GaussNormal<scalar>()*3.0 + 1.5,
R.GaussNormal<scalar>()*0.25 + 4.0,
R.GaussNormal<scalar>()*3.0 - 1.5
),
vector(1.0, 2.0, 5.0)
);
}
Info<< "Mean " << dV.mean() << nl
<< "Median " << dV.median()
<< endl;
dV.write("Distribution_vector_test");
}
// {
// // labelVector
// Distribution<labelVector> dLV(labelVector::one*10);
// label randomDistributionTestSize = 2000000;
// Info<< nl << "Distribution<labelVector>" << nl
// << "Sampling "
// << randomDistributionTestSize
// << " times from uniform distribution."
// << endl;
// for (label i = 0; i < randomDistributionTestSize; i++)
// {
// dLV.add
// (
// labelVector
// (
// R.integer(-1000, 1000),
// R.integer(-5000, 5000),
// R.integer(-2000, 7000)
// )
// );
// }
// Info<< "Mean " << dLV.mean() << nl
// << "Median " << dLV.median()
// << endl;
// dLV.write("Distribution_labelVector_test");
// }
{
// tensor
Distribution<tensor> dT(tensor::one*1e-2);
label randomDistributionTestSize = 2000000;
Info<< nl << "Distribution<tensor>" << nl
<< "Sampling "
<< randomDistributionTestSize
<< " times from uniform distribution."
<< endl;
for (label i = 0; i < randomDistributionTestSize; i++)
{
dT.add(R.sample01<tensor>());
}
Info<< "Mean " << dT.mean() << nl
<< "Median " << dT.median()
<< endl;
dT.write("Distribution_tensor_test");
}
{
// symmTensor
Distribution<symmTensor> dSyT(symmTensor::one*1e-2);
label randomDistributionTestSize = 2000000;
Info<< nl << "Distribution<symmTensor>" << nl
<< "Sampling "
<< randomDistributionTestSize
<< " times from uniform distribution."
<< endl;
for (label i = 0; i < randomDistributionTestSize; i++)
{
dSyT.add(R.sample01<symmTensor>());
}
Info<< "Mean " << dSyT.mean() << nl
<< "Median " << dSyT.median()
<< endl;
dSyT.write("Distribution_symmTensor_test");
}
{
// sphericalTensor
Distribution<sphericalTensor> dSpT(sphericalTensor::one*1e-2);
label randomDistributionTestSize = 50000000;
Info<< nl << "Distribution<sphericalTensor>" << nl
<< "Sampling "
<< randomDistributionTestSize
<< " times from uniform distribution."
<< endl;
for (label i = 0; i < randomDistributionTestSize; i++)
{
dSpT.add(R.sample01<sphericalTensor>());
}
Info<< "Mean " << dSpT.mean() << nl
<< "Median " << dSpT.median()
<< endl;
dSpT.write("Distribution_sphericalTensor_test");
}
Info<< nl << "End" << nl << endl;
return 0;
}
// ************************************************************************* //
Test-DynamicField.C
EXE = $(FOAM_USER_APPBIN)/Test-DynamicField
/* EXE_INC = -I$(LIB_SRC)/finiteVolume/lnInclude */
/* EXE_LIBS = -lfiniteVolume */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment