Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Prasetya, S.W.B. (Wishnu)
t3
Commits
fd82c632
Commit
fd82c632
authored
Feb 17, 2019
by
ISWB Prasetya
Browse files
some tweaks to analytics
parent
f7fa5643
Changes
10
Hide whitespace changes
Inline
Side-by-side
src/Sequenic/T3/DerivativeSuiteGens/Gen2/G2.java
View file @
fd82c632
...
...
@@ -303,14 +303,14 @@ public class G2 {
void
record_and_report_WorkListStatistics
(
int
topIterationCount
)
{
if
(
topIterationCount
<=
0
)
{
// when called first time, create the tables
Analytics
.
registerDataTable
(
worklistStats_tablename
(),
DataTable
.
DOUBLE
,
Analytics
.
registerDataTable
(
worklistStats_tablename
(),
"iteration"
,
"target"
,
"generation"
,
"triedseqs"
,
"suitesize"
,
"cov"
);
Analytics
.
registerDataTable
(
worklistTotCovStats_tablename
(),
DataTable
.
DOUBLE
,
Analytics
.
registerDataTable
(
worklistTotCovStats_tablename
(),
"iteration"
,
"totTriedseqs"
,
"totSuitesize"
,
...
...
@@ -329,7 +329,14 @@ public class G2 {
int
size
=
0
;
if
(
ST
.
suite
!=
null
)
size
=
ST
.
suite
.
suite
.
size
()
;
totSuiteSize
+=
size
;
Analytics
.
addRow
(
worklistStats_tablename
(),
topIterationCount
,
ST
.
getName
(),
ST
.
numberOfIterations
(),
ST
.
totNumberOfTriedSequences
,
size
,
tcov
);
s
+=
" "
+
ST
.
getName
()
+
"_"
+
ST
.
getNumOfParams
()
+
", generation:"
+
ST
.
numberOfIterations
()
+
", seqs-tried:"
+
ST
.
totNumberOfTriedSequences
...
...
@@ -339,9 +346,9 @@ public class G2 {
totTargetCov
=
totTargetCov
/
(
double
)
worklist
.
allTargets
.
size
();
Analytics
.
addRow
(
worklistTotCovStats_tablename
(),
(
double
)
topIterationCount
,
(
double
)
totTriedSeqs
,
(
double
)
totSuiteSize
,
topIterationCount
,
totTriedSeqs
,
totSuiteSize
,
totTargetCov
)
;
s
+=
"Tot. tried sequences:"
+
totTriedSeqs
+
"\n"
;
...
...
@@ -352,7 +359,7 @@ public class G2 {
void
record_and_report_prefixStatistics
(
int
prefixRefinementCount
,
int
added
)
{
if
(
prefixRefinementCount
<=
0
)
{
Analytics
.
registerDataTable
(
prefixCovStats_tablename
(),
DataTable
.
DOUBLE
,
Analytics
.
registerDataTable
(
prefixCovStats_tablename
(),
"iteration"
,
"added"
,
"totSize"
,
...
...
@@ -365,19 +372,21 @@ public class G2 {
//int Ntobjs = g2sg.currentPrefixes.tobjs.size() ;
Analytics
.
addRow
(
prefixCovStats_tablename
(),
(
double
)
prefixRefinementCount
,
(
double
)
added
,
(
double
)
size
,
prefixRefinementCount
,
added
,
size
,
cov
);
if
(
added
>
0
)
{
String
tablename
=
prefixesDiversityStats_tablename
()
+
"_iter_"
+
prefixRefinementCount
;
Analytics
.
registerDataTable
(
tablename
,
DataTable
.
DOUBLE
,
Analytics
.
registerDataTable
(
tablename
,
"depth"
,
"diversitySum"
);
"diversitySum"
,
"avrgBranchDegr"
);
for
(
ObjStructure
o
:
g2sg
.
currentPrefixes
.
tobjs
)
{
if
(
o
==
null
)
Analytics
.
addRow
(
tablename
,
0
d
,
0
d
)
;
else
Analytics
.
addRow
(
tablename
,(
double
)
o
.
depth
,
o
.
diversitySum
);
if
(
o
==
null
)
Analytics
.
addRow
(
tablename
,
0
,
0
d
)
;
else
Analytics
.
addRow
(
tablename
,
o
.
getDepth
(),
o
.
getDiversitySum
(),
o
.
getAvrgBranchingDegree
());
//System.out.println(">>> objstructure " + o.toString()) ;
}
}
...
...
src/Sequenic/T3/DerivativeSuiteGens/Gen2/ObjStructure.java
View file @
fd82c632
...
...
@@ -43,12 +43,31 @@ public class ObjStructure {
Node
root
=
new
Node
()
;
// for statistics:
public
int
depth
;
public
double
diversitySum
;
private
Integer
depth
=
null
;
public
Integer
getDepth
()
{
if
(
depth
==
null
)
{
depth
=
root
.
getDepth
()
;
}
return
depth
;
}
private
Double
diversitySum
=
null
;
public
Double
getDiversitySum
()
{
if
(
diversitySum
==
null
)
{
diversitySum
=
root
.
diversityMetric
()
;
}
return
diversitySum
;
}
private
Double
avrgBranchingDegree
=
null
;
public
Double
getAvrgBranchingDegree
()
{
if
(
avrgBranchingDegree
==
null
)
{
avrgBranchingDegree
=
root
.
getBranchingDegrees
().
stream
().
collect
(
Collectors
.
averagingDouble
(
i
->
(
double
)
i
))
;
}
return
avrgBranchingDegree
;
}
static
public
class
Node
{
List
<
Arrow
>
next
;
Node
()
{
next
=
new
LinkedList
<
Arrow
>()
;
}
List
<
Arrow
>
next
=
new
LinkedList
<
Arrow
>()
;
Node
()
{
}
void
linkto
(
Node
n
,
String
fname
)
{
Arrow
a
=
new
Arrow
()
;
a
.
fieldname
=
fname
;
...
...
@@ -91,6 +110,21 @@ public class ObjStructure {
return
next
.
stream
().
collect
(
Collectors
.
summingDouble
(
arrow
->
arrow
.
target
.
diversityMetric
()))
;
}
List
<
Integer
>
getBranchingDegrees
()
{
List
<
Integer
>
result
=
new
LinkedList
<
Integer
>()
;
result
.
add
(
next
.
size
())
;
for
(
Arrow
A
:
next
)
{
result
.
addAll
(
A
.
target
.
getBranchingDegrees
())
;
}
/*
System.out.print(">>>> branch degrees: ") ;
for (Integer k : result) {
System.out.print(", " + k) ;
}
System.out.println("") ; */
return
result
;
}
void
show
(
StringBuilder
s
,
int
indent
)
{
s
.
append
(
" -->"
)
;
int
k
=
0
;
...
...
@@ -113,7 +147,12 @@ public class ObjStructure {
}
return
false
;
}
List
<
Integer
>
getBranchingDegrees
()
{
List
<
Integer
>
result
=
new
LinkedList
<
Integer
>()
;
//result.add(0) ;
return
result
;
}
void
show
(
StringBuilder
s
,
int
indent
)
{
s
.
append
(
" : "
+
value
)
;
}
...
...
@@ -184,8 +223,8 @@ public class ObjStructure {
super
()
;
List
visited
=
new
LinkedList
()
;
constructWorker
(
maxdepth
,
o
,
root
,
"root"
,
visited
)
;
depth
=
root
.
getDepth
()
;
diversitySum
=
root
.
diversityMetric
()
;
//
depth = root.getDepth() ;
//
diversitySum = root.diversityMetric() ;
}
private
static
void
constructWorker
(
int
depth
,
Object
o
,
Node
parent
,
String
fname
,
List
visited
)
{
...
...
src/Sequenic/T3/DerivativeSuiteGens/Gen2/Test_G2_SimpleSortedList.java
→
src/Sequenic/T3/DerivativeSuiteGens/Gen2/Test_G2_Simple
Int
SortedList.java
View file @
fd82c632
package
Sequenic.T3.DerivativeSuiteGens.Gen2
;
public
class
Test_G2_SimpleSortedList
{
import
Sequenic.T3.ReplayCmd
;
public
class
Test_G2_SimpleIntSortedList
{
static
G2Config
config
()
{
G2Config
config
=
new
G2Config
()
;
...
...
@@ -20,8 +22,13 @@ public class Test_G2_SimpleSortedList {
G2
.
generateSuites
(
"Sequenic.T3.Examples.SimpleIntSortedList"
,
config
(),
120000
)
;
}
public
static
void
main
(
String
[]
args
)
throws
Exception
{
public
static
void
replayG2Regression
()
throws
Throwable
{
ReplayCmd
.
main
(
"--bulk=trace(.*)SimpleIntSortedList(.*)tr --regressionmode /Users/iswbprasetya/tmp/t3"
)
;
}
public
static
void
main
(
String
[]
args
)
throws
Throwable
{
genWithG2
()
;
//replayG2Regression() ;
}
}
src/Sequenic/T3/DerivativeSuiteGens/Gen2/Test_G2_forTriangle.java
View file @
fd82c632
...
...
@@ -45,10 +45,14 @@ public class Test_G2_forTriangle {
ReplayCmd
.
main
(
"--bulk=trace(.*)Triangle(.*)tr --runall /Users/iswbprasetya/tmp/t3"
)
;
}
public
static
void
replayG2Regression
()
throws
Throwable
{
ReplayCmd
.
main
(
"--bulk=trace(.*)Triangle(.*)tr --regressionmode /Users/iswbprasetya/tmp/t3"
)
;
}
static
public
void
main
(
String
[]
args
)
throws
Throwable
{
//test_one_bareG2() ;
//test_two_bareG2() ;
genWithG2
()
;
//replayG2() ;
genWithG2
()
;
//replayG2
Regression
() ;
}
}
src/Sequenic/T3/Examples/SimpleIntSortedListTest.java
0 → 100644
View file @
fd82c632
package
Sequenic.T3.Examples
;
import
static
org
.
junit
.
Assert
.
fail
;
import
org.junit.Test
;
import
Sequenic.T3.OracleError
;
import
Sequenic.T3.ReplayCmd
;
public
class
SimpleIntSortedListTest
{
public
static
void
replayG2
()
throws
Throwable
{
try
{
ReplayCmd
.
main
(
"--bulk=trace(.*)Sorted(.*)tr --regressionmode /Users/iswbprasetya/tmp/t3"
)
;
}
catch
(
OracleError
e
)
{
System
.
out
.
println
(
">>>>> an oracle is violated!"
)
;
fail
(
"CUT violted an oracle"
+
e
.
getMessage
())
;
}
}
@Test
public
void
test1
()
throws
Throwable
{
replayG2
()
;
}
}
src/Sequenic/T3/Examples/Triangle1Test.java
0 → 100644
View file @
fd82c632
package
Sequenic.T3.Examples
;
import
static
org
.
junit
.
Assert
.
fail
;
import
org.junit.Test
;
import
Sequenic.T3.OracleError
;
import
Sequenic.T3.ReplayCmd
;
public
class
Triangle1Test
{
public
static
void
replayG2
()
throws
Throwable
{
try
{
ReplayCmd
.
main
(
"--bulk=trace(.*)Triangle(.*)tr --regressionmode /Users/iswbprasetya/tmp/t3"
)
;
}
catch
(
OracleError
e
)
{
System
.
out
.
println
(
">>> an oracle is violated!"
)
;
fail
(
"CUT violted an oracle"
+
e
.
getMessage
())
;
}
}
@Test
public
void
test1
()
throws
Throwable
{
replayG2
()
;
}
}
src/Sequenic/T3/Info/Analytics/Analytics.java
View file @
fd82c632
...
...
@@ -8,8 +8,8 @@ public class Analytics {
static
Map
<
String
,
DataTable
>
mydata
=
new
HashMap
<
String
,
DataTable
>()
;
public
static
void
registerDataTable
(
String
name
,
boolean
isInteger
,
String
...
columnNames
)
{
DataTable
dt
=
new
DataTable
(
columnNames
.
length
,
isInteger
)
;
public
static
void
registerDataTable
(
String
name
,
String
...
columnNames
)
{
DataTable
dt
=
new
DataTable
(
columnNames
.
length
)
;
dt
.
addHeaderRow
(
columnNames
)
;
mydata
.
put
(
name
,
dt
)
;
}
...
...
@@ -22,15 +22,14 @@ public class Analytics {
return
mydata
.
get
(
name
)
;
}
public
static
void
addRow
(
String
tableName
,
Integer
...
values
)
{
/**
* The added values should be either an integer, a double, or a string.
*/
public
static
void
addRow
(
String
tableName
,
Object
...
values
)
{
DataTable
dt
=
mydata
.
get
(
tableName
)
;
if
(
dt
!=
null
)
dt
.
addRow
(
values
)
;
}
public
static
void
addRow
(
String
tableName
,
Double
...
values
)
{
DataTable
dt
=
mydata
.
get
(
tableName
)
;
if
(
dt
!=
null
)
dt
.
addRow
(
values
)
;
}
public
static
void
clear
()
{
mydata
.
clear
();
...
...
@@ -53,13 +52,13 @@ public class Analytics {
public
static
void
main
(
String
[]
args
)
throws
IOException
{
Analytics
.
clear
();
Analytics
.
registerDataTable
(
"temperature"
,
DataTable
.
DOUBLE
,
"temp"
,
"day"
);
Analytics
.
registerDataTable
(
"fun"
,
DataTable
.
DOUBLE
,
"happy"
,
"day"
);
Analytics
.
addRow
(
"temperature"
,
30
d
,
1
d
)
;
Analytics
.
addRow
(
"temperature"
,
31
d
,
2
d
)
;
Analytics
.
addRow
(
"fun"
,
99
d
,
1
d
)
;
Analytics
.
addRow
(
"fun"
,
0
d
,
2
d
)
;
Analytics
.
save
(
"/Users/iswbprasetya/tmp/t3"
,
"myda
y
a"
,
"xxx"
)
;
Analytics
.
registerDataTable
(
"temperature"
,
"temp"
,
"day"
);
Analytics
.
registerDataTable
(
"fun"
,
"happy"
,
"day"
);
Analytics
.
addRow
(
"temperature"
,
30
d
,
1
)
;
Analytics
.
addRow
(
"temperature"
,
31
d
,
2
)
;
Analytics
.
addRow
(
"fun"
,
99
d
,
1
)
;
Analytics
.
addRow
(
"fun"
,
0
d
,
2
)
;
Analytics
.
save
(
"/Users/iswbprasetya/tmp/t3"
,
"myda
t
a"
,
"xxx"
)
;
}
}
src/Sequenic/T3/Info/Analytics/DataTable.java
View file @
fd82c632
...
...
@@ -13,18 +13,14 @@ import java.util.* ;
*/
public
class
DataTable
{
public
static
final
boolean
INTEGER
=
true
;
public
static
final
boolean
DOUBLE
=
false
;
int
dimension
;
public
String
separator
=
":"
;
boolean
isInteger
=
false
;
String
[]
headerrow
=
null
;
List
<
Number
[]>
rows
=
new
LinkedList
<
Number
[]>()
;
//
should contain either doubles or integers
List
<
Object
[]>
rows
=
new
LinkedList
<
Object
[]>()
;
//
elements should be either a string, integer, or double
public
DataTable
(
int
dimension
,
boolean
isInteger
)
{
public
DataTable
(
int
dimension
)
{
this
.
dimension
=
dimension
;
this
.
isInteger
=
isInteger
;
}
public
DataTable
addHeaderRow
(
String
...
columnNames
)
{
...
...
@@ -33,20 +29,12 @@ public class DataTable {
return
this
;
}
public
DataTable
addRow
(
Integer
...
values
)
{
if
(!
isInteger
)
throw
new
IllegalArgumentException
()
;
public
DataTable
addRow
(
Object
...
values
)
{
if
(
values
.
length
!=
dimension
)
throw
new
IllegalArgumentException
()
;
rows
.
add
(
values
)
;
return
this
;
}
public
DataTable
addRow
(
Double
...
values
)
{
if
(
isInteger
)
throw
new
IllegalArgumentException
()
;
if
(
values
.
length
!=
dimension
)
throw
new
IllegalArgumentException
()
;
rows
.
add
(
values
)
;
return
this
;
}
public
String
toString
()
{
StringBuffer
s
=
new
StringBuffer
()
;
if
(
headerrow
!=
null
)
{
...
...
@@ -56,7 +44,7 @@ public class DataTable {
}
s
.
append
(
"\n"
)
;
}
for
(
Number
[]
R
:
rows
)
{
for
(
Object
[]
R
:
rows
)
{
for
(
int
k
=
0
;
k
<
dimension
;
k
++)
{
if
(
k
>
0
)
s
.
append
(
separator
)
;
s
.
append
(
R
[
k
].
toString
())
;
...
...
@@ -76,9 +64,9 @@ public class DataTable {
public
static
void
main
(
String
[]
args
)
throws
IOException
{
DataTable
dt
=
new
DataTable
(
2
,
DataTable
.
DOUBLE
)
;
DataTable
dt
=
new
DataTable
(
2
)
;
dt
.
addHeaderRow
(
"x"
,
"y"
)
;
dt
.
addRow
(
23
d
,
40
d
)
;
dt
.
addRow
(
23
,
40
d
)
;
dt
.
addRow
(
0.01d
,
9.000000001d
)
;
System
.
out
.
println
(
dt
.
toString
())
;
dt
.
save
(
"/Users/iswbprasetya/tmp/t3/somedata.txt"
)
;
...
...
src/Sequenic/T3/Oracle/StrDumpEqOracle.java
View file @
fd82c632
...
...
@@ -129,7 +129,8 @@ public class StrDumpEqOracle extends Oracle {
String
o_
=
(
String
)
o
;
int
N
=
o_
.
length
()
;
if
(
N
>
8
)
s
.
append
(
o_
.
substring
(
0
,
8
)
+
"%"
+
N
)
;
return
s
.
append
(
o_
)
;
else
s
.
append
(
o_
)
;
return
s
;
}
// if the flag is turned on, convert double, float, long, int abstractly
...
...
@@ -237,16 +238,28 @@ public class StrDumpEqOracle extends Oracle {
double
d
=
0
;
if
(
C
==
Double
.
class
)
d
=
(
double
)
o
;
if
(
C
==
Float
.
class
)
d
=
(
double
)
((
float
)
o
)
;
String
s
=
"100%"
+
(
""
+
Math
.
floor
(
Math
.
abs
(
d
/
100
))).
length
()
;
if
(
d
<
0
)
s
=
"-"
+
s
;
String
s
=
""
+
d
;
try
{
// this should not fail... but just to be sure let's put it in a try-catch
Double
d0
=
Math
.
floor
(
d
)
;
Double
delta
=
d
-
d0
;
d
=
d0
+
Math
.
round
(
delta
*
10000
d
)
/
10000
d
;
s
=
""
+
d
;
}
catch
(
Exception
e
)
{
}
return
s
;
}
if
(
C
==
Long
.
class
||
C
==
Integer
.
class
)
{
long
x
=
0
;
if
(
C
==
Long
.
class
)
x
=
(
long
)
o
;
if
(
C
==
Integer
.
class
)
x
=
(
long
)((
int
)
o
)
;
String
s
=
"100%"
+
(
""
+
Math
.
abs
(
x
/
100
)).
length
()
;
if
(
x
<
0
)
s
=
"-"
+
s
;
String
s
=
""
+
x
;
if
(
x
<
Short
.
MIN_VALUE
||
x
>
Short
.
MAX_VALUE
)
{
String
z
=
""
+
Math
.
abs
(
x
/
100
)
;
int
start
=
Math
.
max
(
0
,
z
.
length
()
-
3
)
;
s
=
"100%"
+
z
.
length
()
+
"%"
+
z
.
substring
(
start
)
;
if
(
x
<
0
)
s
=
"-"
+
s
;
}
return
s
;
}
return
null
;
...
...
@@ -262,7 +275,11 @@ public class StrDumpEqOracle extends Oracle {
Maybe
<
Object
>
returnObj
,
Maybe
<
Throwable
>
thrownException
)
{
//System.out.println(">>> checking an oracle..., RET: " + returnObj + " vs " + expectedReturnedObj) ;
//System.out.println(">>> checking an oracle..., ") ;
//System.out.println(" RET: " + returnObj + " vs expected (encoded): " + expectedReturnedObj) ;
//System.out.println(" tobj:" + receiverObj + " vs expected (encoded): " + expectedReceiverObj) ;
//System.out.println(" exc: " + thrownException + " vs expected (encoded): " + expectedException) ;
if
(
thrownException
!=
null
&&
expectedException
!=
null
)
{
String
s
=
ExcStrDump
(
thrownException
.
val
)
;
if
(!
s
.
equals
(
expectedException
.
val
))
{
...
...
@@ -272,6 +289,7 @@ public class StrDumpEqOracle extends Oracle {
msg
=
"Exc-violation: expecting no exception, but got "
+
s
;
else
msg
=
"Exc-violation: expecting: "
+
expectedException
.
val
+
" to be thrown, but getting: "
+
s
;
//System.out.println(">>> violating oracle: " + msg) ;
return
msg
;
}
}
...
...
@@ -283,6 +301,7 @@ public class StrDumpEqOracle extends Oracle {
String
msg
=
"Expecting: "
+
expectedReceiverObj
.
val
+
"\nbut getting: "
+
s
;
// Logger.getLogger(CONSTANTS.T3loggerName).info(">>> An StrDumbEqOracle detects a VIOLATION!")
// ;
//System.out.println(">>> violating oracle: " + msg) ;
return
msg
;
}
}
catch
(
Exception
e
)
{
...
...
@@ -310,6 +329,9 @@ public class StrDumpEqOracle extends Oracle {
static
public
void
main
(
String
args
[])
{
System
.
out
.
println
(
stringtify
(
"hello world"
,
5
))
;
System
.
out
.
println
(
stringtify
(
"hi"
,
5
))
;
System
.
out
.
println
(
stringtify
(
null
,
5
))
;
System
.
out
.
println
(
stringtify
(
new
Object
(),
5
))
;
System
.
out
.
println
(
stringtify
(
new
Sequenic
.
T3
.
Examples
.
Item
(
"Foo"
),
5
))
;
...
...
src/Sequenic/T3/Sequence/Datatype/SUITE.java
View file @
fd82c632
...
...
@@ -384,12 +384,14 @@ public class SUITE implements Serializable {
// collect all potential steps that can be injected with oracles:
List
<
STEP
>
injectibles
=
new
LinkedList
<
STEP
>()
;
for
(
STEP
s
:
sigma
.
steps
)
{
if
(
s
instanceof
CONSTRUCTOR
||
s
instanceof
METHOD
||
s
instanceof
UPDATE_FIELD
)
if
(
s
instanceof
CONSTRUCTOR
||
s
instanceof
METHOD
)
injectibles
.
add
(
s
)
;
}
//System.err.println(">>> " + injectibles.size());
// drop the prefix that are not to be injected:
int
ignoredPrefix
=
injectibles
.
size
()
-
injectionSuffixLength
;
int
ignoredPrefix
=
injectibles
.
size
()
-
1
;
// at least one step to be enhanced
if
(
injectionSuffixLength
>
0
)
ignoredPrefix
=
injectibles
.
size
()
-
injectionSuffixLength
;
for
(
int
k
=
0
;
k
<
ignoredPrefix
;
k
++)
injectibles
.
remove
(
0
)
;
boolean
injected
=
false
;
...
...
@@ -473,6 +475,7 @@ public class SUITE implements Serializable {
else
step_
.
oracle
=
new
StrDumpEqOracle
(
receiver
,
returned
,
exc
)
;
injected
=
true
;
}
/*
else if (laststep instanceof UPDATE_FIELD) {
//System.out.println(">>> injecting field oracle...");
UPDATE_FIELD step_ = (UPDATE_FIELD) laststep ;
...
...
@@ -483,6 +486,7 @@ public class SUITE implements Serializable {
injected = true ;
}
}
*/
if
(!
injected
)
return
false
;
...
...
@@ -517,7 +521,8 @@ public class SUITE implements Serializable {
boolean
injected
=
injectOracles
(
CUT
,
seq
,
injectionSuffixLength
)
;
if
(
injected
)
k
++
;
}
Logger
.
getLogger
(
CONSTANTS
.
T3loggerName
).
info
(
"** Injected oracles on "
+
k
+
" sequences of a suite of "
+
CUTname
+
" ..."
)
;
Logger
.
getLogger
(
CONSTANTS
.
T3loggerName
).
info
(
"** Injected oracles on "
+
k
+
" sequences (out of "
+
suite
.
size
()
+
") of a suite of "
+
CUTname
+
" ..."
)
;
}
/**
...
...
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