Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
G
gpucloudsim
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
LPDS
gpucloudsim
Commits
ea31eb0e
Commit
ea31eb0e
authored
Feb 01, 2016
by
Manoel Campos
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improved documentation of classes from package org.cloudbus.cloudsim.util.
parent
a2543cff
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
596 additions
and
511 deletions
+596
-511
Cloudlet.java
...loudsim/src/main/java/org/cloudbus/cloudsim/Cloudlet.java
+2
-2
ExecutionTimeMeasurer.java
...ava/org/cloudbus/cloudsim/util/ExecutionTimeMeasurer.java
+24
-9
MathUtil.java
...im/src/main/java/org/cloudbus/cloudsim/util/MathUtil.java
+51
-46
WorkloadFileReader.java
...n/java/org/cloudbus/cloudsim/util/WorkloadFileReader.java
+515
-450
WorkloadModel.java
...c/main/java/org/cloudbus/cloudsim/util/WorkloadModel.java
+4
-4
No files found.
modules/cloudsim/src/main/java/org/cloudbus/cloudsim/Cloudlet.java
View file @
ea31eb0e
...
...
@@ -86,7 +86,7 @@ public class Cloudlet {
private
int
reservationId
=
-
1
;
/** Indicates if transaction history records for this Cloudlet
* i
t
to be outputted. */
* i
s
to be outputted. */
private
final
boolean
record
;
/** Stores the operating system line separator. */
...
...
@@ -1015,7 +1015,7 @@ public class Cloudlet {
/**
* Gets the total length (across all PEs) of this Cloudlet.
* It considers the {@link #cloudletLength} of the cloudlet to be execute
* It considers the {@link #cloudletLength} of the cloudlet to be execute
d
* in each Pe and the {@link #numberOfPes}.<br/>
*
* For example, setting the cloudletLenght as 10000 MI and {@link #numberOfPes}
...
...
modules/cloudsim/src/main/java/org/cloudbus/cloudsim/util/ExecutionTimeMeasurer.java
View file @
ea31eb0e
...
...
@@ -12,30 +12,44 @@ import java.util.HashMap;
import
java.util.Map
;
/**
*
The class for measuring the execution time
.
*
Measurement of execution times of CloudSim's methods
.
*
* @author Anton Beloglazov
* @since CloudSim Toolkit 3.0
*/
public
class
ExecutionTimeMeasurer
{
/** The execution times. */
/** A map of execution times where each key
* represents the name of the method/process being its
* execution time computed and each key is the
* time the method/process started (in milliseconds).
* Usually, this name is the method/process name, making
* easy to identify the execution times into the map.
*
* @todo The name of the attribute doesn't match with what it stores.
* It in fact stores the method/process start time,
* no the time it spent executing.
*/
private
final
static
Map
<
String
,
Long
>
executionTimes
=
new
HashMap
<
String
,
Long
>();
/**
* Start.
* Start measuring the execution time of a method/process.
* Usually this method has to be called at the first line of the method
* that has to be its execution time measured.
*
* @param name the name
* @param name the name of the method/process being measured.
* @see #executionTimes
*/
public
static
void
start
(
String
name
)
{
getExecutionTimes
().
put
(
name
,
System
.
currentTimeMillis
());
}
/**
*
End
.
*
Finalizes measuring the execution time of a method/process
.
*
* @param name the name
* @return the double
* @param name the name of the method/process being measured.
* @see #executionTimes
* @return the time the method/process spent in execution (in seconds)
*/
public
static
double
end
(
String
name
)
{
double
time
=
(
System
.
currentTimeMillis
()
-
getExecutionTimes
().
get
(
name
))
/
1000.0
;
...
...
@@ -44,9 +58,10 @@ public class ExecutionTimeMeasurer {
}
/**
* Gets the execution times.
* Gets
map
the execution times.
*
* @return the execution times
* @return the execution times map
* @see #executionTimes
*/
public
static
Map
<
String
,
Long
>
getExecutionTimes
()
{
return
executionTimes
;
...
...
modules/cloudsim/src/main/java/org/cloudbus/cloudsim/util/MathUtil.java
View file @
ea31eb0e
...
...
@@ -20,13 +20,14 @@ import org.apache.commons.math3.stat.regression.SimpleRegression;
*
* @author Anton Beloglazov
* @since CloudSim Toolkit 3.0
* @todo Using Java 8 Stream, some methods here can be improved or removed.
*/
public
class
MathUtil
{
/**
* Sums a list of numbers.
*
* @param list the list
* @param list the list
of numbers
* @return the double
*/
public
static
double
sum
(
final
List
<?
extends
Number
>
list
)
{
...
...
@@ -38,10 +39,12 @@ public class MathUtil {
}
/**
* List to array.
*
Converts a
List to array.
*
* @param list the list
* @param list the list
of numbers
* @return the double[]
* @todo The method {@link List#toArray()} could be used directly
* instead of creating this method.
*/
public
static
double
[]
listToArray
(
final
List
<?
extends
Number
>
list
)
{
double
[]
array
=
new
double
[
list
.
size
()];
...
...
@@ -52,9 +55,9 @@ public class MathUtil {
}
/**
* Gets the median.
* Gets the median
from a list of numbers
.
*
* @param list the list
* @param list the list
of numbers
* @return the median
*/
public
static
double
median
(
final
List
<
Double
>
list
)
{
...
...
@@ -62,9 +65,9 @@ public class MathUtil {
}
/**
* Gets the median.
* Gets the median
from an array of numbers
.
*
* @param list the
list
* @param list the
array of numbers
*
* @return the median
*/
...
...
@@ -73,11 +76,10 @@ public class MathUtil {
}
/**
* Returns
descriptive statistics for the
list of numbers.
* Returns
an object to compute descriptive statistics for an
list of numbers.
*
* @param list
* - the list of numbers. Must not be null.
* @return - descriptive statistics for the list of numbers.
* @param list the list of numbers. Must not be null.
* @return descriptive statistics for the list of numbers.
*/
public
static
DescriptiveStatistics
getStatistics
(
final
List
<
Double
>
list
)
{
// Get a DescriptiveStatistics instance
...
...
@@ -91,10 +93,10 @@ public class MathUtil {
}
/**
* Returns
descriptive statistics for the
array of numbers.
* Returns
an object to compute descriptive statistics for an
array of numbers.
*
* @param list
-
the array of numbers. Must not be null.
* @return
-
descriptive statistics for the array of numbers.
* @param list the array of numbers. Must not be null.
* @return descriptive statistics for the array of numbers.
*/
public
static
DescriptiveStatistics
getStatistics
(
final
double
[]
list
)
{
// Get a DescriptiveStatistics instance
...
...
@@ -108,9 +110,9 @@ public class MathUtil {
}
/**
* Gets the average.
* Gets the average
from a list of numbers
.
*
* @param list the list
* @param list the list
of numbers
*
* @return the average
*/
...
...
@@ -123,10 +125,10 @@ public class MathUtil {
}
/**
*
Variance
.
*
Gets the Variance from a list of numbers
.
*
* @param list the list
* @return the
doubl
e
* @param list the list
of numbers
* @return the
varianc
e
*/
public
static
double
variance
(
final
List
<
Double
>
list
)
{
long
n
=
0
;
...
...
@@ -145,19 +147,19 @@ public class MathUtil {
}
/**
* Gets the standard deviation.
* Gets the standard deviation
from a list of numbers
.
*
* @param list the list
* @return the
double
* @param list the list
of numbers
* @return the
standard deviation
*/
public
static
double
stDev
(
final
List
<
Double
>
list
)
{
return
Math
.
sqrt
(
variance
(
list
));
}
/**
* Gets the mad.
* Gets the mad
from a array of numbers
.
*
* @param data the
data
* @param data the
array of numbers
* @return the mad
*/
public
static
double
mad
(
final
double
[]
data
)
{
...
...
@@ -174,9 +176,9 @@ public class MathUtil {
}
/**
* Gets the I
QR
.
* Gets the I
nterquartile Range (IQR) from an array of numbers
.
*
* @param data the
data
* @param data the
array of numbers
* @return the IQR
*/
public
static
double
iqr
(
final
double
[]
data
)
{
...
...
@@ -187,10 +189,11 @@ public class MathUtil {
}
/**
* Count non zero beginning of the data.
* Counts the number of values different of zero at the beginning of
* an array.
*
* @param data the
data
* @return the
int
* @param data the
array of numbers
* @return the
number of values different of zero at the beginning of the array
*/
public
static
int
countNonZeroBeginning
(
final
double
[]
data
)
{
int
i
=
data
.
length
-
1
;
...
...
@@ -203,10 +206,10 @@ public class MathUtil {
}
/**
*
Count shortest row.
*
Gets the length of the shortest row in a given matrix
*
* @param data the data
* @return the
int
* @param data the data
matrix
* @return the
length of the shortest row int he matrix
*/
public
static
int
countShortestRow
(
final
double
[][]
data
)
{
int
minLength
=
0
;
...
...
@@ -219,10 +222,10 @@ public class MathUtil {
}
/**
* Trim
zero tail
.
* Trim
s zeros at the end of an array
.
*
* @param data the data
* @return the
double[]
* @param data the data
array
* @return the
trimmed array
*/
public
static
double
[]
trimZeroTail
(
final
double
[]
data
)
{
return
Arrays
.
copyOfRange
(
data
,
0
,
countNonZeroBeginning
(
data
));
...
...
@@ -231,7 +234,7 @@ public class MathUtil {
/**
* Gets the loess parameter estimates.
*
* @param y the y
* @param y the y
array
* @return the loess parameter estimates
*/
public
static
double
[]
getLoessParameterEstimates
(
final
double
[]
y
)
{
...
...
@@ -291,7 +294,7 @@ public class MathUtil {
/**
* Gets the robust loess parameter estimates.
*
* @param y the y
* @param y the y
array
* @return the robust loess parameter estimates
*/
public
static
double
[]
getRobustLoessParameterEstimates
(
final
double
[]
y
)
{
...
...
@@ -318,10 +321,11 @@ public class MathUtil {
}
/**
* Gets the tricube weigts.
* Gets the tricube weigt
h
s.
*
* @param n the n
* @return the tricube weigts
* @param n the number of weights
* @return an array of tricube weigths with n elements
* @todo The word "weight" is misspelled in the method name.
*/
public
static
double
[]
getTricubeWeigts
(
final
int
n
)
{
double
[]
weights
=
new
double
[
n
];
...
...
@@ -340,10 +344,11 @@ public class MathUtil {
}
/**
* Gets the tricube bisquare weigts.
* Gets the tricube bisquare weigt
h
s.
*
* @param residuals the residuals
* @return the tricube bisquare weigts
* @param residuals the residuals array
* @return the tricube bisquare weigths
* @todo The word "weight" is misspelled in the method name.
*/
public
static
double
[]
getTricubeBisquareWeigts
(
final
double
[]
residuals
)
{
int
n
=
residuals
.
length
;
...
...
@@ -363,10 +368,10 @@ public class MathUtil {
}
/**
*
Abs.
*
Gets the absolute values of an array of values
*
* @param data the
data
* @return
the double[]
* @param data the
array of values
* @return
a new array with the absolute value of each element in the given array.
*/
public
static
double
[]
abs
(
final
double
[]
data
)
{
double
[]
result
=
new
double
[
data
.
length
];
...
...
modules/cloudsim/src/main/java/org/cloudbus/cloudsim/util/WorkloadFileReader.java
View file @
ea31eb0e
...
...
@@ -25,85 +25,137 @@ import org.cloudbus.cloudsim.UtilizationModel;
import
org.cloudbus.cloudsim.UtilizationModelFull
;
/**
* This class is responsible for reading resource traces from a file and creating a list of jobs.
* <p>
* This class is responsible for reading resource traces from a file and creating a list of jobs
* ({@link Cloudlet Cloudlets}).
* <p/>
* <b>NOTE:</b>
* <ul>
* <li>This class can only take <tt>one</tt> trace file of the following format: <i>ASCII text, zip,
* gz.</i>
* <li>If you need to load multiple trace files, then you need to create multiple instances of this
* class <tt>each with a unique
* entity name</tt>.
* <li>If size of the trace file is huge or contains lots of traces please increase the JVM heap
* class <tt>each with a unique entity name</tt>.
* <li>If size of the trace file is huge or contains lots of traces, please increase the JVM heap
* size accordingly by using <tt>java -Xmx</tt> option when running the simulation.
* <li>The default job file size for sending to and receiving from a resource is
* {@link gridsim.net.Link#DEFAULT_MTU}. However, you can specify the file size by using
* {@link #set
Gri
dletFileSize(int)}.
* {@link #set
Clou
dletFileSize(int)}.
* <li>A job run time is only for 1 PE <tt>not</tt> the total number of allocated PEs. Therefore, a
*
Gri
dlet length is also calculated for 1 PE.<br>
*
Clou
dlet length is also calculated for 1 PE.<br>
* For example, job #1 in the trace has a run time of 100 seconds for 2 processors. This means each
* processor runs job #1 for 100 seconds, if the processors have the same specification.
* </ul>
* <p>
* By default, this class follows the standard workload format as specified in <a
* href="http://www.cs.huji.ac.il/labs/parallel/workload/">
* http://www.cs.huji.ac.il/labs/parallel/workload/</a> <br>
* However, you can use other format by calling the below methods before running the simulation:
*
* @todo The last item in the list above is not true. The cloudlet length is not
* divided by the number of PEs. If there is more than 1 PE, all PEs run the same
* number of MI as specified in the {@link Cloudlet#cloudletLength} attribute.
* See {@link Cloudlet#setNumberOfPes(int)} method documentation.
*
* <p/>
* By default, this class follows the standard workload format as specified in
* <a href="http://www.cs.huji.ac.il/labs/parallel/workload/">
* http://www.cs.huji.ac.il/labs/parallel/workload/</a> <br/>
* However, you can use other format by calling the methods below before running the simulation:
* <ul>
* <li> {@link #setComment(String)}
* <li> {@link #setField(int, int, int, int, int)}
* </ul>
*
* @author Anthony Sulistio and Marcos Dias de Assuncao
* @author Anthony Sulistio
* @author Marcos Dias de Assuncao
* @since 5.0
*
* @see Workload
*/
public
class
WorkloadFileReader
implements
WorkloadModel
{
/**
* Trace file name.
*/
private
final
File
file
;
private
final
File
file
;
// file name
/**
* The Cloudlet's PE rating (in MIPS), considering that all PEs of a Cloudlet
* have the same rate.
*/
private
final
int
rating
;
private
final
int
rating
;
// a PE rating
/**
* List of Cloudlets created from the trace {@link #file}.
*/
private
ArrayList
<
Cloudlet
>
jobs
=
null
;
private
ArrayList
<
Cloudlet
>
jobs
=
null
;
// a list for getting all the
// Gridlets
/* Index of fields from the Standard Workload Format. */
// using Standard Workload Format
private
int
JOB_NUM
=
1
-
1
;
// job number
/**
* Field index of job number.
*/
private
int
JOB_NUM
=
1
-
1
;
private
int
SUBMIT_TIME
=
2
-
1
;
// submit time of a Gridlet
/**
* Field index of submit time of a job.
*/
private
int
SUBMIT_TIME
=
2
-
1
;
private
final
int
RUN_TIME
=
4
-
1
;
// running time of a Gridlet
/**
* Field index of running time of a job.
*/
private
final
int
RUN_TIME
=
4
-
1
;
private
final
int
NUM_PROC
=
5
-
1
;
// number of processors needed for a
/**
* Field index of number of processors needed for a job.
*/
private
final
int
NUM_PROC
=
5
-
1
;
// Gridlet
private
int
REQ_NUM_PROC
=
8
-
1
;
// required number of processors
/**
* Field index of required number of processors.
*/
private
int
REQ_NUM_PROC
=
8
-
1
;
private
int
REQ_RUN_TIME
=
9
-
1
;
// required running time
/**
* Field index of required running time.
*/
private
int
REQ_RUN_TIME
=
9
-
1
;
private
final
int
USER_ID
=
12
-
1
;
// if of user who submitted the job
/**
* Field index of user who submitted the job.
*/
private
final
int
USER_ID
=
12
-
1
;
private
final
int
GROUP_ID
=
13
-
1
;
// if of group of the user who
/**
* Field index of group of the user who submitted the job.
*/
private
final
int
GROUP_ID
=
13
-
1
;
// submitted the
// job
private
int
MAX_FIELD
=
18
;
// max number of field in the trace file
/**
* Max number of fields in the trace file.
*/
private
int
MAX_FIELD
=
18
;
private
String
COMMENT
=
";"
;
// a string that denotes the start of a
/**
* A string that denotes the start of a comment.
*/
private
String
COMMENT
=
";"
;
// comment
private
static
final
int
IRRELEVANT
=
-
1
;
// irrelevant number
/**
* If the field index of the job number ({@link #JOB_NUM}) is equals
* to this constant, it means the number of the job doesn't have to be
* gotten from the trace file, but has to be generated by this workload generator
* class.
*/
private
static
final
int
IRRELEVANT
=
-
1
;
private
String
[]
fieldArray
=
null
;
// a temp array storing all the fields
/**
* A temp array storing all the fields read from a line of the trace file.
*/
private
String
[]
fieldArray
=
null
;
/**
* Create a new {@link WorkloadFileReader}
object.
* Create a new WorkloadFileReader
object.
*
* @param fileName the workload trace filename in one of the following format: <i>ASCII text,
* zip, gz.</i>
* @param rating the resource's PE rating
* @param fileName the workload trace filename in one of the following formats:
* <i>ASCII text, zip, gz.</i>
* @param rating the cloudlet's PE rating (in MIPS), considering that all PEs
* of a cloudlet have the same rate
* @throws FileNotFoundException
* @throws IllegalArgumentException This happens for the following conditions:
* <ul>
...
...
@@ -130,9 +182,10 @@ public class WorkloadFileReader implements WorkloadModel {
}
/**
* Reads job information from a given file
.
* Reads job information from a trace file and generates the respective cloudlets
.
*
* @return the list of gridlets read from the file; <code>null</code> in case of failure.
* @return the list of cloudlets read from the file; <code>null</code> in case of failure.
* @see #file
*/
@Override
public
ArrayList
<
Cloudlet
>
generateWorkload
()
{
...
...
@@ -143,6 +196,10 @@ public class WorkloadFileReader implements WorkloadModel {
fieldArray
=
new
String
[
MAX_FIELD
];
try
{
/*@todo It would be implemented
using specific classes to avoid using ifs.
If a new format is included, the code has to be
changed to include another if*/
if
(
file
.
getName
().
endsWith
(
".gz"
))
{
readGZIPFile
(
file
);
}
else
if
(
file
.
getName
().
endsWith
(
".zip"
))
{
...
...
@@ -159,7 +216,7 @@ public class WorkloadFileReader implements WorkloadModel {
}
/**
* I
dentifies the start of a comment line.
* Sets the string that i
dentifies the start of a comment line.
*
* @param cmt a character that denotes the start of a comment, e.g. ";" or "#"
* @return <code>true</code> if it is successful, <code>false</code> otherwise
...
...
@@ -178,14 +235,15 @@ public class WorkloadFileReader implements WorkloadModel {
/**
* Tells this class what to look in the trace file. This method should be called before the
* start of the simulation.
* <p
>
* <p/
>
* By default, this class follows the standard workload format as specified in <a
* href="http://www.cs.huji.ac.il/labs/parallel/workload/">
* http://www.cs.huji.ac.il/labs/parallel/workload/</a> <br>
* However, you can use other format by calling this method.
* <p
>
* <p/
>
* The parameters must be a positive integer number starting from 1. A special case is where
* <tt>jobNum == -1</tt>, meaning the job or gridlet ID starts at 1.
* <tt>jobNum == {@link #IRRELEVANT}</tt>, meaning the job or cloudlet ID will be generate
* by the Workload class, instead of reading from the trace file.
*
* @param maxField max. number of field/column in one row
* @param jobNum field/column number for locating the job ID
...
...
@@ -249,13 +307,15 @@ public class WorkloadFileReader implements WorkloadModel {
// ------------------- PRIVATE METHODS -------------------
/**
* Creates a Gridlet with the given information and adds to the list
* Creates a Cloudlet with the given information and adds to the list of {@link #jobs}.
*
* @param id a Gridlet ID
* @param submitTime Gridlet's submit time
* @param runTime Gridlet's run time
* @param numProc number of processors
* @param id a Cloudlet ID
* @param submitTime Cloudlet's submit time
* @param runTime The number of seconds the Cloudlet has to run. Considering that
* and the {@link #rating}, the {@link Cloudlet#cloudletLength} is computed.
* @param numProc number of Cloudlet's PEs
* @param reqRunTime user estimated run time
* (@todo the parameter is not being used and it is not clear what it is)
* @param userID user id
* @param groupID user's group id
* @pre id >= 0
...
...
@@ -263,6 +323,7 @@ public class WorkloadFileReader implements WorkloadModel {
* @pre runTime >= 0
* @pre numProc > 0
* @post $none
* @see #rating
*/
private
void
createJob
(
final
int
id
,
...
...
@@ -288,12 +349,15 @@ public class WorkloadFileReader implements WorkloadModel {
}
/**
* Extracts relevant information from a given array
* Extracts relevant information from a given array of fields,
* representing a line from the trace file, and create a cloudlet
* using this information.
*
* @param array an array of String
* @param line a
line number
* @param array the array of fields generated from a line of the trace file.
* @param line the
line number
* @pre array != null
* @pre line > 0
* @todo The name of the method doesn't describe what it in fact does.
*/
private
void
extractField
(
final
String
[]
array
,
final
int
line
)
{
try
{
...
...
@@ -352,10 +416,11 @@ public class WorkloadFileReader implements WorkloadModel {
}
/**
* Breaks a line of string into many fields.
* Breaks a line from the trace file into many fields into the
* {@link #fieldArray}.
*
* @param line a line of string
* @param lineNum a
line number
* @param line a line from the trace file
* @param lineNum the
line number
* @pre line != null
* @pre lineNum > 0
* @post $none
...
...
@@ -389,7 +454,7 @@ public class WorkloadFileReader implements WorkloadModel {
}
/**
* Reads a text file one line at the time
* Reads traces from a text file, one line at a time.
*
* @param fl a file name
* @return <code>true</code> if successful, <code>false</code> otherwise.
...
...
@@ -422,7 +487,7 @@ public class WorkloadFileReader implements WorkloadModel {
}
/**
* Reads a gzip file one line at the time
* Reads traces from a gzip file, one line at a time.
*
* @param fl a gzip file name
* @return <code>true</code> if successful; <code>false</code> otherwise.
...
...
@@ -455,7 +520,7 @@ public class WorkloadFileReader implements WorkloadModel {
}
/**
* Reads a Zip fil
e.
* Reads traces from a Zip file, one line at a tim
e.
*
* @param fl a zip file name
* @return <code>true</code> if reading a file is successful; <code>false</code> otherwise.
...
...
modules/cloudsim/src/main/java/org/cloudbus/cloudsim/util/WorkloadModel.java
View file @
ea31eb0e
...
...
@@ -13,8 +13,8 @@ import java.util.List;
import
org.cloudbus.cloudsim.Cloudlet
;
/**
*
This interface d
efines what a workload model should provide. A workload model generates a list of
* jobs that can be dispatched to a resource by {@link Workload}.
*
D
efines what a workload model should provide. A workload model generates a list of
* jobs
({@link Cloudlet Cloudlets})
that can be dispatched to a resource by {@link Workload}.
*
* @author Marcos Dias de Assuncao
* @since 5.0
...
...
@@ -25,9 +25,9 @@ import org.cloudbus.cloudsim.Cloudlet;
public
interface
WorkloadModel
{
/**
*
Returns a list with the jobs generated by the workload
.
*
Generates a list of jobs to be executed ({@link Cloudlet Cloudlets})
.
*
* @return a list with the jobs generated by the workload.
* @return a list with the jobs generated by the workload
or null in case of failure
.
*/
List
<
Cloudlet
>
generateWorkload
();
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment