Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
abc
Overview
Overview
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
lvzhengyang
abc
Commits
2d0a8fb4
Commit
2d0a8fb4
authored
May 19, 2016
by
Alan Mishchenko
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improving SMT-LIB parser.
parent
07d074fd
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
372 additions
and
2 deletions
+372
-2
src/base/wlc/wlcReadSmt.c
+370
-0
src/opt/sfm/sfmArea.c
+2
-2
No files found.
src/base/wlc/wlcReadSmt.c
View file @
2d0a8fb4
...
...
@@ -41,6 +41,9 @@ struct Smt_Prs_t_
Vec_Int_t
vStack
;
// current node on each level
//Vec_Wec_t vDepth; // objects on each level
Vec_Wec_t
vObjs
;
// objects
int
NameCount
;
int
nDigits
;
Vec_Int_t
vTempFans
;
// error handling
char
ErrorStr
[
1000
];
};
...
...
@@ -671,6 +674,371 @@ finish:
/**Function*************************************************************
Synopsis []
Description []
SideEffects []
SeeAlso []
***********************************************************************/
char
*
Smt_PrsGenName
(
Smt_Prs_t
*
p
)
{
static
char
Buffer
[
16
];
sprintf
(
Buffer
,
"_%0*X_"
,
p
->
nDigits
,
++
p
->
NameCount
);
return
Buffer
;
}
int
Smt_PrsBuild2_rec
(
Wlc_Ntk_t
*
pNtk
,
Smt_Prs_t
*
p
,
int
iNode
)
{
if
(
Smt_EntryIsName
(
iNode
)
)
{
int
fFound
,
iObj
;
char
*
pStr
=
Abc_NamStr
(
p
->
pStrs
,
Abc_Lit2Var
(
iNode
));
if
(
pStr
[
0
]
==
'#'
)
{
int
i
,
nDigits
,
nBits
=
-
1
,
iObj
=
-
1
;
Vec_IntClear
(
&
p
->
vTempFans
);
if
(
pStr
[
1
]
==
'b'
)
// binary
{
nBits
=
strlen
(
pStr
+
2
);
Vec_IntFill
(
&
p
->
vTempFans
,
Abc_BitWordNum
(
nBits
),
0
);
for
(
i
=
0
;
i
<
nBits
;
i
++
)
if
(
pStr
[
2
+
i
]
==
'1'
)
Abc_InfoSetBit
(
(
unsigned
*
)
Vec_IntArray
(
&
p
->
vTempFans
),
nBits
-
1
-
i
);
else
if
(
pStr
[
2
+
i
]
!=
'0'
)
return
0
;
}
else
if
(
pStr
[
1
]
==
'x'
)
// hexadecimal
{
nBits
=
strlen
(
pStr
+
2
)
*
4
;
Vec_IntFill
(
&
p
->
vTempFans
,
Abc_BitWordNum
(
nBits
),
0
);
nDigits
=
Abc_TtReadHexNumber
(
(
word
*
)
Vec_IntArray
(
&
p
->
vTempFans
),
pStr
+
2
);
if
(
nDigits
!=
(
nBits
+
3
)
/
4
)
return
0
;
}
else
return
0
;
// create constant node
iObj
=
Smt_PrsCreateNode
(
pNtk
,
WLC_OBJ_CONST
,
0
,
nBits
,
&
p
->
vTempFans
,
Smt_PrsGenName
(
p
)
);
return
iObj
;
}
iObj
=
Abc_NamStrFindOrAdd
(
pNtk
->
pManName
,
pStr
,
&
fFound
);
assert
(
fFound
);
return
iObj
;
}
else
{
Vec_Int_t
*
vRoots
,
*
vRoots1
,
*
vFans3
;
int
iRoot0
,
iRoot1
,
Fan
,
Fan3
,
k
;
assert
(
!
Smt_EntryIsName
(
iNode
)
);
vRoots
=
Smt_EntryNode
(
p
,
iNode
);
iRoot0
=
Vec_IntEntry
(
vRoots
,
0
);
if
(
Smt_EntryIsName
(
iRoot0
)
)
{
char
*
pName
,
*
pStr0
=
Abc_NamStr
(
p
->
pStrs
,
Abc_Lit2Var
(
iRoot0
));
int
fSigned
=
0
,
iObj
;
if
(
pStr0
[
0
]
==
'l'
&&
pStr0
[
1
]
==
'e'
&&
pStr0
[
2
]
==
't'
&&
pStr0
[
3
]
==
'\0'
)
{
// let ((s35550 (bvor s48 s35549)))
assert
(
Vec_IntSize
(
vRoots
)
==
3
);
assert
(
Smt_VecEntryIsType
(
vRoots
,
0
,
SMT_PRS_LET
)
);
// get parts
iRoot1
=
Vec_IntEntry
(
vRoots
,
1
);
assert
(
!
Smt_EntryIsName
(
iRoot1
)
);
vRoots1
=
Smt_EntryNode
(
p
,
iRoot1
);
//if ( Smt_VecEntryIsType(vRoots1, 0, SMT_PRS_LET) )
// continue;
// iterate through the parts
Vec_IntForEachEntry
(
vRoots1
,
Fan
,
k
)
{
// s35550 (bvor s48 s35549)
Fan
=
Vec_IntEntry
(
vRoots1
,
0
);
assert
(
!
Smt_EntryIsName
(
Fan
)
);
vFans3
=
Smt_EntryNode
(
p
,
Fan
);
// get the name
Fan3
=
Vec_IntEntry
(
vFans3
,
0
);
assert
(
Smt_EntryIsName
(
Fan3
)
);
pName
=
Smt_EntryName
(
p
,
Fan3
);
// get function
Fan3
=
Vec_IntEntry
(
vFans3
,
1
);
assert
(
!
Smt_EntryIsName
(
Fan3
)
);
// solve the problem
iObj
=
Smt_PrsBuild2_rec
(
pNtk
,
p
,
Fan3
);
if
(
iObj
==
0
)
return
0
;
// create buffer
Vec_IntFill
(
&
p
->
vTempFans
,
1
,
iObj
);
return
Smt_PrsCreateNode
(
pNtk
,
WLC_OBJ_BUF
,
0
,
1
,
&
p
->
vTempFans
,
pName
);
}
}
else
if
(
pStr0
[
0
]
==
'_'
)
{
int
iRoot1
=
Vec_IntEntry
(
vRoots
,
1
);
char
*
pStr1
=
Abc_NamStr
(
p
->
pStrs
,
Abc_Lit2Var
(
iRoot1
));
if
(
pStr1
[
0
]
==
'b'
&&
pStr1
[
1
]
==
'v'
)
{
int
iObj
=
0
;
return
iObj
;
}
else
{
int
Type1
,
Range
=
-
1
,
fSigned
=
0
;
// call the other branch
assert
(
Vec_IntSize
(
vRoots
)
==
2
);
iObj
=
Smt_PrsBuild2_rec
(
pNtk
,
p
,
Vec_IntEntry
(
vRoots
,
1
)
);
if
(
iObj
==
0
)
return
0
;
Vec_IntFill
(
&
p
->
vTempFans
,
1
,
iObj
);
// find out this branch
Type1
=
Abc_Lit2Var
(
iRoot1
);
if
(
Type1
==
WLC_OBJ_BIT_SIGNEXT
||
Type1
==
WLC_OBJ_BIT_ZEROPAD
||
Type1
==
WLC_OBJ_ROTATE_R
||
Type1
==
WLC_OBJ_ROTATE_L
)
{
int
iRoot2
=
Vec_IntEntry
(
vRoots
,
2
);
char
*
pStr2
=
Abc_NamStr
(
p
->
pStrs
,
Abc_Lit2Var
(
iRoot2
));
int
Num
=
atoi
(
pStr2
);
fSigned
=
(
Type1
==
WLC_OBJ_BIT_SIGNEXT
);
Range
=
Num
+
Wlc_ObjRange
(
Wlc_NtkObj
(
pNtk
,
iObj
)
);
Vec_IntPush
(
&
p
->
vTempFans
,
Num
);
}
else
if
(
Type1
==
WLC_OBJ_BIT_SELECT
)
{
int
iRoot2
=
Vec_IntEntry
(
vRoots
,
2
);
int
iRoot3
=
Vec_IntEntry
(
vRoots
,
3
);
char
*
pStr2
=
Abc_NamStr
(
p
->
pStrs
,
Abc_Lit2Var
(
iRoot2
));
char
*
pStr3
=
Abc_NamStr
(
p
->
pStrs
,
Abc_Lit2Var
(
iRoot3
));
int
Num1
=
atoi
(
pStr1
);
int
Num2
=
atoi
(
pStr2
);
assert
(
Num1
>=
Num2
);
fSigned
=
(
Type1
==
WLC_OBJ_BIT_SIGNEXT
);
Range
=
(
Num1
-
Num2
+
1
)
+
Wlc_ObjRange
(
Wlc_NtkObj
(
pNtk
,
iObj
)
);
Vec_IntPushTwo
(
&
p
->
vTempFans
,
Num1
,
Num2
);
}
else
assert
(
0
);
iObj
=
Smt_PrsCreateNode
(
pNtk
,
Type1
,
fSigned
,
Range
,
&
p
->
vTempFans
,
Smt_PrsGenName
(
p
)
);
return
iObj
;
}
}
else
{
Vec_Int_t
*
vFanins
;
int
i
,
Fan
,
fSigned
=
0
,
Range
,
Type0
;
int
iObj
=
Abc_NamStrFind
(
pNtk
->
pManName
,
pStr0
);
if
(
iObj
>
0
)
return
iObj
;
Type0
=
Smt_StrToType
(
pStr0
,
&
fSigned
);
// collect fanins
vFanins
=
Vec_IntAlloc
(
100
);
Vec_IntForEachEntryStart
(
vRoots
,
Fan
,
i
,
1
)
{
iObj
=
Smt_PrsBuild2_rec
(
pNtk
,
p
,
Fan
);
if
(
iObj
==
0
)
{
Vec_IntFree
(
vFanins
);
return
0
;
}
Vec_IntPush
(
vFanins
,
iObj
);
}
// update specialized nodes
assert
(
Type0
!=
WLC_OBJ_BIT_SIGNEXT
&&
Type0
!=
WLC_OBJ_BIT_ZEROPAD
&&
Type0
!=
WLC_OBJ_BIT_SELECT
&&
Type0
!=
WLC_OBJ_ROTATE_R
&&
Type0
!=
WLC_OBJ_ROTATE_L
);
// find range
Range
=
0
;
if
(
Type0
>=
WLC_OBJ_LOGIC_NOT
&&
Type0
<=
WLC_OBJ_REDUCT_XOR
)
Range
=
1
;
else
if
(
Type0
==
WLC_OBJ_BIT_CONCAT
)
{
Vec_IntForEachEntry
(
vFanins
,
Fan
,
i
)
Range
+=
Wlc_ObjRange
(
Wlc_NtkObj
(
pNtk
,
Fan
)
);
}
else
if
(
Type0
==
WLC_OBJ_MUX
)
{
int
*
pArray
=
Vec_IntArray
(
vFanins
);
assert
(
Vec_IntSize
(
vFanins
)
==
3
);
ABC_SWAP
(
int
,
pArray
[
1
],
pArray
[
2
]
);
iObj
=
Vec_IntEntry
(
vFanins
,
1
);
Range
=
Wlc_ObjRange
(
Wlc_NtkObj
(
pNtk
,
iObj
)
);
}
else
// to determine range, look at the first argument
{
iObj
=
Vec_IntEntry
(
vFanins
,
0
);
Range
=
Wlc_ObjRange
(
Wlc_NtkObj
(
pNtk
,
iObj
)
);
}
// create node
assert
(
Range
>
0
);
iObj
=
Smt_PrsCreateNode
(
pNtk
,
Type0
,
fSigned
,
Range
,
vFanins
,
Smt_PrsGenName
(
p
)
);
Vec_IntFree
(
vFanins
);
return
iObj
;
}
}
else
assert
(
0
);
// return Smt_PrsBuild2_rec( pNtk, p, iRoot0 );
return
0
;
}
}
Wlc_Ntk_t
*
Smt_PrsBuild2
(
Smt_Prs_t
*
p
)
{
Wlc_Ntk_t
*
pNtk
;
Vec_Int_t
*
vFansRoot
,
*
vFans
,
*
vFans2
;
Vec_Int_t
*
vAsserts
=
Vec_IntAlloc
(
100
);
int
i
,
Fan
,
iObj
,
NameId
,
Range
,
Status
,
nBits
=
0
;
char
*
pName
,
*
pRange
,
*
pValue
;
// start network and create primary inputs
pNtk
=
Wlc_NtkAlloc
(
p
->
pName
,
1000
);
pNtk
->
pManName
=
Abc_NamStart
(
1000
,
24
);
// collect top-level asserts
vFansRoot
=
Vec_WecEntry
(
&
p
->
vObjs
,
0
);
Vec_IntForEachEntry
(
vFansRoot
,
Fan
,
i
)
{
assert
(
!
Smt_EntryIsName
(
Fan
)
);
vFans
=
Smt_VecEntryNode
(
p
,
vFansRoot
,
i
);
Fan
=
Vec_IntEntry
(
vFans
,
0
);
assert
(
Smt_EntryIsName
(
Fan
)
);
// create variables
if
(
Abc_Lit2Var
(
Fan
)
==
SMT_PRS_DECLARE_FUN
)
{
assert
(
Vec_IntSize
(
vFans
)
==
4
);
assert
(
Smt_VecEntryIsType
(
vFans
,
0
,
SMT_PRS_DECLARE_FUN
)
);
// get name
Fan
=
Vec_IntEntry
(
vFans
,
1
);
assert
(
Smt_EntryIsName
(
Fan
)
);
pName
=
Smt_EntryName
(
p
,
Fan
);
// skip ()
Fan
=
Vec_IntEntry
(
vFans
,
2
);
assert
(
!
Smt_EntryIsName
(
Fan
)
);
// check type (Bool or BitVec)
Fan
=
Vec_IntEntry
(
vFans
,
3
);
if
(
Smt_EntryIsName
(
Fan
)
)
{
//(declare-fun s1 () Bool)
assert
(
!
strcmp
(
"Bool"
,
Smt_VecEntryName
(
p
,
vFans
,
3
))
);
Range
=
1
;
}
else
{
// (declare-fun s1 () (_ BitVec 64))
// get range
Fan
=
Vec_IntEntry
(
vFans
,
3
);
assert
(
!
Smt_EntryIsName
(
Fan
)
);
vFans2
=
Smt_EntryNode
(
p
,
Fan
);
assert
(
Vec_IntSize
(
vFans2
)
==
3
);
assert
(
!
strcmp
(
"_"
,
Smt_VecEntryName
(
p
,
vFans2
,
0
))
);
assert
(
!
strcmp
(
"BitVec"
,
Smt_VecEntryName
(
p
,
vFans2
,
1
))
);
Fan
=
Vec_IntEntry
(
vFans2
,
2
);
assert
(
Smt_EntryIsName
(
Fan
)
);
pRange
=
Smt_EntryName
(
p
,
Fan
);
Range
=
atoi
(
pRange
);
}
// create node
iObj
=
Wlc_ObjAlloc
(
pNtk
,
WLC_OBJ_PI
,
0
,
Range
-
1
,
0
);
NameId
=
Abc_NamStrFindOrAdd
(
pNtk
->
pManName
,
pName
,
NULL
);
assert
(
iObj
==
NameId
);
// save values
Vec_IntPush
(
&
pNtk
->
vValues
,
NameId
);
Vec_IntPush
(
&
pNtk
->
vValues
,
nBits
);
Vec_IntPush
(
&
pNtk
->
vValues
,
Range
);
nBits
+=
Range
;
}
// create constants
else
if
(
Abc_Lit2Var
(
Fan
)
==
SMT_PRS_DEFINE_FUN
)
{
assert
(
Vec_IntSize
(
vFans
)
==
5
);
assert
(
Smt_VecEntryIsType
(
vFans
,
0
,
SMT_PRS_DEFINE_FUN
)
);
// get name
Fan
=
Vec_IntEntry
(
vFans
,
1
);
assert
(
Smt_EntryIsName
(
Fan
)
);
pName
=
Smt_EntryName
(
p
,
Fan
);
// skip ()
Fan
=
Vec_IntEntry
(
vFans
,
2
);
assert
(
!
Smt_EntryIsName
(
Fan
)
);
// check type (Bool or BitVec)
Fan
=
Vec_IntEntry
(
vFans
,
3
);
if
(
Smt_EntryIsName
(
Fan
)
)
{
// (define-fun s_2 () Bool false)
assert
(
!
strcmp
(
"Bool"
,
Smt_VecEntryName
(
p
,
vFans
,
3
))
);
Range
=
1
;
pValue
=
Smt_VecEntryName
(
p
,
vFans
,
4
);
if
(
!
strcmp
(
"false"
,
pValue
)
)
pValue
=
"#b0"
;
else
if
(
!
strcmp
(
"true"
,
pValue
)
)
pValue
=
"#b1"
;
else
assert
(
0
);
Status
=
Smt_PrsBuildConstant
(
pNtk
,
pValue
,
Range
,
pName
);
}
else
{
// (define-fun s702 () (_ BitVec 4) #xe)
// (define-fun s1 () (_ BitVec 8) (bvneg #x7f))
// get range
Fan
=
Vec_IntEntry
(
vFans
,
3
);
assert
(
!
Smt_EntryIsName
(
Fan
)
);
vFans2
=
Smt_VecEntryNode
(
p
,
vFans
,
3
);
assert
(
Vec_IntSize
(
vFans2
)
==
3
);
assert
(
!
strcmp
(
"_"
,
Smt_VecEntryName
(
p
,
vFans2
,
0
))
);
assert
(
!
strcmp
(
"BitVec"
,
Smt_VecEntryName
(
p
,
vFans2
,
1
))
);
// get range
Fan
=
Vec_IntEntry
(
vFans2
,
2
);
assert
(
Smt_EntryIsName
(
Fan
)
);
pRange
=
Smt_EntryName
(
p
,
Fan
);
Range
=
atoi
(
pRange
);
// get constant
Fan
=
Vec_IntEntry
(
vFans
,
4
);
Status
=
Smt_PrsBuildNode
(
pNtk
,
p
,
Fan
,
Range
,
pName
);
}
if
(
!
Status
)
{
Wlc_NtkFree
(
pNtk
);
pNtk
=
NULL
;
goto
finish
;
}
}
// collect assertion outputs
else
if
(
Abc_Lit2Var
(
Fan
)
==
SMT_PRS_ASSERT
)
{
//(assert ; no quantifiers
// (let ((s2 (bvsge s0 s1)))
// (not s2)))
//(assert (not (= s0 #x00)))
assert
(
Vec_IntSize
(
vFans
)
==
2
);
assert
(
Smt_VecEntryIsType
(
vFans
,
0
,
SMT_PRS_ASSERT
)
);
iObj
=
Smt_PrsBuild2_rec
(
pNtk
,
p
,
Vec_IntEntry
(
vFans
,
1
)
);
if
(
iObj
==
0
)
{
Wlc_NtkFree
(
pNtk
);
pNtk
=
NULL
;
goto
finish
;
}
Vec_IntPush
(
vAsserts
,
iObj
);
}
// issue warnings about unknown dirs
else
if
(
Abc_Lit2Var
(
Fan
)
>=
SMT_PRS_END
)
{
printf
(
"Ignoring directive
\"
%s
\"
.
\n
"
,
Smt_EntryName
(
p
,
Fan
)
);
continue
;
}
}
// build AND of asserts
if
(
Vec_IntSize
(
vAsserts
)
==
1
)
iObj
=
Smt_PrsCreateNode
(
pNtk
,
WLC_OBJ_BUF
,
0
,
1
,
vAsserts
,
"miter_output"
);
else
{
iObj
=
Smt_PrsCreateNode
(
pNtk
,
WLC_OBJ_BIT_CONCAT
,
0
,
Vec_IntSize
(
vAsserts
),
vAsserts
,
NULL
);
Vec_IntFill
(
vAsserts
,
1
,
iObj
);
iObj
=
Smt_PrsCreateNode
(
pNtk
,
WLC_OBJ_REDUCT_AND
,
0
,
1
,
vAsserts
,
"miter_output"
);
}
Wlc_ObjSetCo
(
pNtk
,
Wlc_NtkObj
(
pNtk
,
iObj
),
0
);
// create nameIDs
vFans
=
Vec_IntStartNatural
(
Wlc_NtkObjNumMax
(
pNtk
)
);
Vec_IntAppend
(
&
pNtk
->
vNameIds
,
vFans
);
Vec_IntFree
(
vFans
);
//Wlc_NtkReport( pNtk, NULL );
finish:
// cleanup
Vec_IntFree
(
vAsserts
);
return
pNtk
;
}
/**Function*************************************************************
...
...
@@ -767,6 +1135,7 @@ static inline void Smt_PrsFree( Smt_Prs_t * p )
if
(
p
->
pStrs
)
Abc_NamDeref
(
p
->
pStrs
);
Vec_IntErase
(
&
p
->
vStack
);
Vec_IntErase
(
&
p
->
vTempFans
);
//Vec_WecErase( &p->vDepth );
Vec_WecErase
(
&
p
->
vObjs
);
ABC_FREE
(
p
);
...
...
@@ -865,6 +1234,7 @@ void Smt_PrsPrintParser( Smt_Prs_t * p )
{
// Vec_WecPrint( &p->vDepth, 0 );
Smt_PrsPrintParser_rec
(
p
,
0
,
0
);
p
->
nDigits
=
Abc_Base16Log
(
Vec_WecSize
(
&
p
->
vObjs
)
);
}
/**Function*************************************************************
...
...
src/opt/sfm/sfmArea.c
View file @
2d0a8fb4
...
...
@@ -219,7 +219,7 @@ int Abc_ObjHasDupFanouts( Abc_Obj_t * pObj )
int
Abc_ObjChangeEval
(
Abc_Obj_t
*
pObj
,
Vec_Int_t
*
vInfo
,
Vec_Int_t
*
vFirst
,
int
InvArea
,
int
*
pfUseInv
)
{
Abc_Obj_t
*
pNext
;
Mio_Gate_t
*
pGate
=
(
Mio_Gate_t
*
)
pObj
->
pData
;
//
Mio_Gate_t * pGate = (Mio_Gate_t *)pObj->pData;
int
iFanCell
,
iNodeCell
=
Mio_GateReadCell
(
(
Mio_Gate_t
*
)
pObj
->
pData
);
int
*
pFanInfo
,
*
pNodeInfo
=
Vec_IntEntryP
(
vInfo
,
Vec_IntEntry
(
vFirst
,
iNodeCell
)
);
int
i
,
fNeedInv
=
0
,
Gain
=
0
,
iFanin
=
Abc_ObjFaninNum
(
pObj
),
fUseInv
=
Abc_NodeIsInv
(
pObj
);
...
...
@@ -267,7 +267,7 @@ int Abc_ObjChangeEval( Abc_Obj_t * pObj, Vec_Int_t * vInfo, Vec_Int_t * vFirst,
void
Abc_ObjChangeUpdate
(
Abc_Obj_t
*
pObj
,
int
iFanin
,
Mio_Cell2_t
*
pCells
,
int
*
pNodeInfo
,
Vec_Int_t
*
vTemp
)
{
int
v
,
Perm
,
iNodeCell
=
pNodeInfo
[
3
*
iFanin
];
Mio_Gate_t
*
pGate
=
(
Mio_Gate_t
*
)
pObj
->
pData
;
//
Mio_Gate_t * pGate = (Mio_Gate_t *)pObj->pData;
//Abc_ObjPrint( stdout, pObj );
//printf( "Replacing fanout %d with %s by %s with fanin %d.\n", Abc_ObjId(pObj), Mio_GateReadName(pGate), Mio_GateReadName((Mio_Gate_t *)pCells[iNodeCell].pMioGate), iFanin );
pObj
->
pData
=
(
Mio_Gate_t
*
)
pCells
[
iNodeCell
].
pMioGate
;
...
...
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