Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
C
cpdt
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
research
cpdt
Commits
a6fa0f0e
Commit
a6fa0f0e
authored
Jul 17, 2012
by
Adam Chlipala
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update for Coq trunk version intended for final 8.4 release
parent
f6842c36
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
25 additions
and
14 deletions
+25
-14
CpdtTactics.v
src/CpdtTactics.v
+1
-1
DataStruct.v
src/DataStruct.v
+2
-2
InductiveTypes.v
src/InductiveTypes.v
+1
-1
MoreDep.v
src/MoreDep.v
+4
-4
StackMachine.v
src/StackMachine.v
+4
-4
Universes.v
src/Universes.v
+13
-2
No files found.
src/CpdtTactics.v
View file @
a6fa0f0e
...
@@ -214,7 +214,7 @@ Ltac dep_destruct E :=
...
@@ -214,7 +214,7 @@ Ltac dep_destruct E :=
let
x
:=
fresh
"x"
in
let
x
:=
fresh
"x"
in
remember
E
as
x
;
simpl
in
x
;
dependent
destruction
x
;
remember
E
as
x
;
simpl
in
x
;
dependent
destruction
x
;
try
match
goal
with
try
match
goal
with
|
[
H
:
_
=
E
|-
_
]
=>
rewrite
<-
H
in
*;
clear
H
|
[
H
:
_
=
E
|-
_
]
=>
try
rewrite
<-
H
in
*;
clear
H
end
.
end
.
(
**
Nuke
all
hypotheses
that
we
can
get
away
with
,
without
invalidating
the
goal
statement
.
*
)
(
**
Nuke
all
hypotheses
that
we
can
get
away
with
,
without
invalidating
the
goal
statement
.
*
)
...
...
src/DataStruct.v
View file @
a6fa0f0e
...
@@ -773,14 +773,14 @@ Fixpoint cfold t (e : exp' t) : exp' t :=
...
@@ -773,14 +773,14 @@ Fixpoint cfold t (e : exp' t) : exp' t :=
|
Plus
e1
e2
=>
|
Plus
e1
e2
=>
let
e1
'
:=
cfold
e1
in
let
e1
'
:=
cfold
e1
in
let
e2
'
:=
cfold
e2
in
let
e2
'
:=
cfold
e2
in
match
e1
'
,
e2
'
return
_
with
match
e1
'
,
e2
'
return
exp
'
Nat
with
|
NConst
n1
,
NConst
n2
=>
NConst
(
n1
+
n2
)
|
NConst
n1
,
NConst
n2
=>
NConst
(
n1
+
n2
)
|
_
,
_
=>
Plus
e1
'
e2
'
|
_
,
_
=>
Plus
e1
'
e2
'
end
end
|
Eq
e1
e2
=>
|
Eq
e1
e2
=>
let
e1
'
:=
cfold
e1
in
let
e1
'
:=
cfold
e1
in
let
e2
'
:=
cfold
e2
in
let
e2
'
:=
cfold
e2
in
match
e1
'
,
e2
'
return
_
with
match
e1
'
,
e2
'
return
exp
'
Bool
with
|
NConst
n1
,
NConst
n2
=>
BConst
(
if
eq_nat_dec
n1
n2
then
true
else
false
)
|
NConst
n1
,
NConst
n2
=>
BConst
(
if
eq_nat_dec
n1
n2
then
true
else
false
)
|
_
,
_
=>
Eq
e1
'
e2
'
|
_
,
_
=>
Eq
e1
'
e2
'
end
end
...
...
src/InductiveTypes.v
View file @
a6fa0f0e
...
@@ -342,7 +342,7 @@ Qed.
...
@@ -342,7 +342,7 @@ Qed.
(
**
The
[
injection
]
tactic
refers
to
a
premise
by
number
,
adding
new
equalities
between
the
corresponding
arguments
of
equated
terms
that
are
formed
with
the
same
constructor
.
We
end
up
needing
to
prove
[
n
=
m
->
n
=
m
]
,
so
it
is
unsurprising
that
a
tactic
named
[
trivial
]
is
able
to
finish
the
proof
.
(
**
The
[
injection
]
tactic
refers
to
a
premise
by
number
,
adding
new
equalities
between
the
corresponding
arguments
of
equated
terms
that
are
formed
with
the
same
constructor
.
We
end
up
needing
to
prove
[
n
=
m
->
n
=
m
]
,
so
it
is
unsurprising
that
a
tactic
named
[
trivial
]
is
able
to
finish
the
proof
.
There
is
also
a
very
useful
tactic
called
%
\
index
{
tactics
!
congruence
}%
[
congruence
]
that
can
prove
this
theorem
immediately
.
[
congruence
]
generalizes
[
discriminate
]
and
[
injection
]
,
and
it
also
adds
reasoning
about
the
general
properties
of
equality
,
such
as
that
a
function
returns
equal
results
on
equal
arguments
.
That
is
,
[
congruence
]
is
a
%
\
index
{
theory
of
equality
and
uninterpreted
functions
}%
_
complete
decision
procedure
for
the
theory
of
equality
and
uninterpreted
functions_
,
plus
some
smarts
about
inductive
types
.
There
is
also
a
very
useful
tactic
called
%
\
index
{
tactics
!
congruence
}%
[
congruence
]
that
can
prove
this
theorem
immediately
.
The
[
congruence
]
tactic
generalizes
[
discriminate
]
and
[
injection
]
,
and
it
also
adds
reasoning
about
the
general
properties
of
equality
,
such
as
that
a
function
returns
equal
results
on
equal
arguments
.
That
is
,
[
congruence
]
is
a
%
\
index
{
theory
of
equality
and
uninterpreted
functions
}%
_
complete
decision
procedure
for
the
theory
of
equality
and
uninterpreted
functions_
,
plus
some
smarts
about
inductive
types
.
%
\
medskip
%
%
\
medskip
%
...
...
src/MoreDep.v
View file @
a6fa0f0e
...
@@ -280,7 +280,7 @@ Definition pairOut t (e : exp t) :=
...
@@ -280,7 +280,7 @@ Definition pairOut t (e : exp t) :=
(
**
There
is
one
important
subtlety
in
this
definition
.
Coq
allows
us
to
use
convenient
ML
-
style
pattern
matching
notation
,
but
,
internally
and
in
proofs
,
we
see
that
patterns
are
expanded
out
completely
,
matching
one
level
of
inductive
structure
at
a
time
.
Thus
,
the
default
case
in
the
[
match
]
above
expands
out
to
one
case
for
each
constructor
of
[
exp
]
besides
[
Pair
]
,
and
the
underscore
in
[
pairOutDefault
_
]
is
resolved
differently
in
each
case
.
From
an
ML
or
Haskell
programmer
'
s
perspective
,
what
we
have
here
is
type
inference
determining
which
code
is
run
(
returning
either
[
None
]
or
[
tt
])
,
which
goes
beyond
what
is
possible
with
type
inference
guiding
parametric
polymorphism
in
Hindley
-
Milner
languages
%
\
index
{
Hindley
-
Milner
}%,
but
is
similar
to
what
goes
on
with
Haskell
type
classes
%
\
index
{
type
classes
}%.
(
**
There
is
one
important
subtlety
in
this
definition
.
Coq
allows
us
to
use
convenient
ML
-
style
pattern
matching
notation
,
but
,
internally
and
in
proofs
,
we
see
that
patterns
are
expanded
out
completely
,
matching
one
level
of
inductive
structure
at
a
time
.
Thus
,
the
default
case
in
the
[
match
]
above
expands
out
to
one
case
for
each
constructor
of
[
exp
]
besides
[
Pair
]
,
and
the
underscore
in
[
pairOutDefault
_
]
is
resolved
differently
in
each
case
.
From
an
ML
or
Haskell
programmer
'
s
perspective
,
what
we
have
here
is
type
inference
determining
which
code
is
run
(
returning
either
[
None
]
or
[
tt
])
,
which
goes
beyond
what
is
possible
with
type
inference
guiding
parametric
polymorphism
in
Hindley
-
Milner
languages
%
\
index
{
Hindley
-
Milner
}%,
but
is
similar
to
what
goes
on
with
Haskell
type
classes
%
\
index
{
type
classes
}%.
With
[
pairOut
]
available
,
we
can
write
[
cfold
]
in
a
straightforward
way
.
There
are
really
no
surprises
beyond
that
Coq
verifies
that
this
code
has
such
an
expressive
type
,
given
the
small
annotation
burden
.
In
some
places
,
we
see
that
Coq
'
s
[
match
]
annotation
inference
is
too
smart
for
its
own
good
,
and
we
have
to
turn
that
inference
off
by
writing
[
return
_
]
.
*
)
With
[
pairOut
]
available
,
we
can
write
[
cfold
]
in
a
straightforward
way
.
There
are
really
no
surprises
beyond
that
Coq
verifies
that
this
code
has
such
an
expressive
type
,
given
the
small
annotation
burden
.
In
some
places
,
we
see
that
Coq
'
s
[
match
]
annotation
inference
is
too
smart
for
its
own
good
,
and
we
have
to
turn
that
inference
with
explicit
[
return
]
clauses
.
*
)
Fixpoint
cfold
t
(
e
:
exp
t
)
:
exp
t
:=
Fixpoint
cfold
t
(
e
:
exp
t
)
:
exp
t
:=
match
e
with
match
e
with
...
@@ -288,14 +288,14 @@ Fixpoint cfold t (e : exp t) : exp t :=
...
@@ -288,14 +288,14 @@ Fixpoint cfold t (e : exp t) : exp t :=
|
Plus
e1
e2
=>
|
Plus
e1
e2
=>
let
e1
'
:=
cfold
e1
in
let
e1
'
:=
cfold
e1
in
let
e2
'
:=
cfold
e2
in
let
e2
'
:=
cfold
e2
in
match
e1
'
,
e2
'
return
_
with
match
e1
'
,
e2
'
return
exp
Nat
with
|
NConst
n1
,
NConst
n2
=>
NConst
(
n1
+
n2
)
|
NConst
n1
,
NConst
n2
=>
NConst
(
n1
+
n2
)
|
_
,
_
=>
Plus
e1
'
e2
'
|
_
,
_
=>
Plus
e1
'
e2
'
end
end
|
Eq
e1
e2
=>
|
Eq
e1
e2
=>
let
e1
'
:=
cfold
e1
in
let
e1
'
:=
cfold
e1
in
let
e2
'
:=
cfold
e2
in
let
e2
'
:=
cfold
e2
in
match
e1
'
,
e2
'
return
_
with
match
e1
'
,
e2
'
return
exp
Bool
with
|
NConst
n1
,
NConst
n2
=>
BConst
(
if
eq_nat_dec
n1
n2
then
true
else
false
)
|
NConst
n1
,
NConst
n2
=>
BConst
(
if
eq_nat_dec
n1
n2
then
true
else
false
)
|
_
,
_
=>
Eq
e1
'
e2
'
|
_
,
_
=>
Eq
e1
'
e2
'
end
end
...
@@ -304,7 +304,7 @@ Fixpoint cfold t (e : exp t) : exp t :=
...
@@ -304,7 +304,7 @@ Fixpoint cfold t (e : exp t) : exp t :=
|
And
e1
e2
=>
|
And
e1
e2
=>
let
e1
'
:=
cfold
e1
in
let
e1
'
:=
cfold
e1
in
let
e2
'
:=
cfold
e2
in
let
e2
'
:=
cfold
e2
in
match
e1
'
,
e2
'
return
_
with
match
e1
'
,
e2
'
return
exp
Bool
with
|
BConst
b1
,
BConst
b2
=>
BConst
(
b1
&&
b2
)
|
BConst
b1
,
BConst
b2
=>
BConst
(
b1
&&
b2
)
|
_
,
_
=>
And
e1
'
e2
'
|
_
,
_
=>
And
e1
'
e2
'
end
end
...
...
src/StackMachine.v
View file @
a6fa0f0e
...
@@ -51,7 +51,7 @@ Inductive exp : Set :=
...
@@ -51,7 +51,7 @@ Inductive exp : Set :=
(
**
Now
we
define
the
type
of
arithmetic
expressions
.
We
write
that
a
constant
may
be
built
from
one
argument
,
a
natural
number
;
and
a
binary
operation
may
be
built
from
a
choice
of
operator
and
two
operand
expressions
.
(
**
Now
we
define
the
type
of
arithmetic
expressions
.
We
write
that
a
constant
may
be
built
from
one
argument
,
a
natural
number
;
and
a
binary
operation
may
be
built
from
a
choice
of
operator
and
two
operand
expressions
.
A
note
for
readers
following
along
in
the
PDF
version
:
%
\
index
{
coqdoc
}%
coqdoc
supports
pretty
-
printing
of
tokens
in
LaTeX
or
HTML
.
Where
you
see
a
right
arrow
character
,
the
source
contains
the
ASCII
text
%
\
texttt
{%
#
<
tt
>
#
->
#
</
tt
>
#
%}%.
Other
examples
of
this
substitution
appearing
in
this
chapter
are
a
double
right
arrow
for
%
\
texttt
{%
#
<
tt
>
#
=>
#
</
tt
>
#
%}%,
the
inverted
%
`
%
#
'
#
A
'
symbol
for
%
\
texttt
{%
#
<
tt
>
#
forall
#
</
tt
>
#
%}%,
and
the
Cartesian
product
%
`
%
#
'
#
X
'
for
%
\
texttt
{%
#
<
tt
>
#
*
#
</
tt
>
#
%}%.
When
in
doubt
about
the
ASCII
version
of
a
symbol
,
you
can
consult
the
chapter
source
code
.
A
note
for
readers
following
along
in
the
PDF
version
:
%
\
index
{
coqdoc
}%
coqdoc
supports
pretty
-
printing
of
tokens
in
%
\
LaTeX
{}%
#
LaTeX
#
or
HTML
.
Where
you
see
a
right
arrow
character
,
the
source
contains
the
ASCII
text
%
\
texttt
{%
#
<
tt
>
#
->
#
</
tt
>
#
%}%.
Other
examples
of
this
substitution
appearing
in
this
chapter
are
a
double
right
arrow
for
%
\
texttt
{%
#
<
tt
>
#
=>
#
</
tt
>
#
%}%,
the
inverted
%
`
%
#
'
#
A
'
symbol
for
%
\
texttt
{%
#
<
tt
>
#
forall
#
</
tt
>
#
%}%,
and
the
Cartesian
product
%
`
%
#
'
#
X
'
for
%
\
texttt
{%
#
<
tt
>
#
*
#
</
tt
>
#
%}%.
When
in
doubt
about
the
ASCII
version
of
a
symbol
,
you
can
consult
the
chapter
source
code
.
%
\
medskip
%
%
\
medskip
%
...
@@ -508,7 +508,7 @@ app_nil_end
...
@@ -508,7 +508,7 @@ app_nil_end
rewrite
(
app_nil_end
(
compile
e
))
.
rewrite
(
app_nil_end
(
compile
e
))
.
(
**
This
time
,
we
explicitly
specify
the
value
of
the
variable
[
l
]
from
the
theorem
statement
,
since
multiple
expressions
of
list
type
appear
in
the
conclusion
.
[
rewrite
]
might
choose
the
wrong
place
to
rewrite
if
we
did
not
specify
which
we
want
.
(
**
This
time
,
we
explicitly
specify
the
value
of
the
variable
[
l
]
from
the
theorem
statement
,
since
multiple
expressions
of
list
type
appear
in
the
conclusion
.
The
[
rewrite
]
tactic
might
choose
the
wrong
place
to
rewrite
if
we
did
not
specify
which
we
want
.
[[
[[
e
:
exp
e
:
exp
...
@@ -562,9 +562,9 @@ The inuitive explanation of [tbinop] is that a [tbinop t1 t2 t] is a binary oper
...
@@ -562,9 +562,9 @@ The inuitive explanation of [tbinop] is that a [tbinop t1 t2 t] is a binary oper
ML
and
Haskell
have
indexed
algebraic
datatypes
.
For
instance
,
their
list
types
are
indexed
by
the
type
of
data
that
the
list
carries
.
However
,
compared
to
Coq
,
ML
and
Haskell
98
place
two
important
restrictions
on
datatype
definitions
.
ML
and
Haskell
have
indexed
algebraic
datatypes
.
For
instance
,
their
list
types
are
indexed
by
the
type
of
data
that
the
list
carries
.
However
,
compared
to
Coq
,
ML
and
Haskell
98
place
two
important
restrictions
on
datatype
definitions
.
First
,
the
indices
of
the
range
of
each
data
constructor
must
be
type
variables
bound
at
the
top
level
of
the
datatype
definition
.
There
is
no
way
to
do
what
we
did
here
,
where
we
,
for
instance
,
say
that
[
TPlus
]
is
a
constructor
building
a
[
tbinop
]
whose
indices
are
all
fixed
at
[
Nat
]
.
%
\
index
{
generalized
algebraic
datatypes
}
\
index
{
GADTs
|
see
{
generalized
algebraic
datatypes
}}%
_
Generalized
algebraic
datatypes_
(
GADT
'
s
)
%~
\
cite
{
GADT
}%
are
a
popular
feature
in
%
\
index
{
GHC
Haskell
}%
GHC
Haskell
and
other
languages
that
removes
this
first
restriction
.
First
,
the
indices
of
the
range
of
each
data
constructor
must
be
type
variables
bound
at
the
top
level
of
the
datatype
definition
.
There
is
no
way
to
do
what
we
did
here
,
where
we
,
for
instance
,
say
that
[
TPlus
]
is
a
constructor
building
a
[
tbinop
]
whose
indices
are
all
fixed
at
[
Nat
]
.
%
\
index
{
generalized
algebraic
datatypes
}
\
index
{
GADTs
|
see
{
generalized
algebraic
datatypes
}}%
_
Generalized
algebraic
datatypes_
(
GADTs
)
%~
\
cite
{
GADT
}%
are
a
popular
feature
in
%
\
index
{
GHC
Haskell
}%
GHC
Haskell
and
other
languages
that
removes
this
first
restriction
.
The
second
restriction
is
not
lifted
by
GADT
'
s
.
In
ML
and
Haskell
,
indices
of
types
must
be
types
and
may
not
be
_
expressions_
.
In
Coq
,
types
may
be
indexed
by
arbitrary
Gallina
terms
.
Type
indices
can
live
in
the
same
universe
as
programs
,
and
we
can
compute
with
them
just
like
regular
programs
.
Haskell
supports
a
hobbled
form
of
computation
in
type
indices
based
on
%
\
index
{
Haskell
}%
multi
-
parameter
type
classes
,
and
recent
extensions
like
type
functions
bring
Haskell
programming
even
closer
to
%
``
%
#
"#real#"
#
%
''
%
functional
programming
with
types
,
but
,
without
dependent
typing
,
there
must
always
be
a
gap
between
how
one
programs
with
types
and
how
one
programs
normally
.
The
second
restriction
is
not
lifted
by
GADTs
.
In
ML
and
Haskell
,
indices
of
types
must
be
types
and
may
not
be
_
expressions_
.
In
Coq
,
types
may
be
indexed
by
arbitrary
Gallina
terms
.
Type
indices
can
live
in
the
same
universe
as
programs
,
and
we
can
compute
with
them
just
like
regular
programs
.
Haskell
supports
a
hobbled
form
of
computation
in
type
indices
based
on
%
\
index
{
Haskell
}%
multi
-
parameter
type
classes
,
and
recent
extensions
like
type
functions
bring
Haskell
programming
even
closer
to
%
``
%
#
"#real#"
#
%
''
%
functional
programming
with
types
,
but
,
without
dependent
typing
,
there
must
always
be
a
gap
between
how
one
programs
with
types
and
how
one
programs
normally
.
*
)
*
)
(
**
We
can
define
a
similar
type
family
for
typed
expressions
,
where
a
term
of
type
[
texp
t
]
can
be
assigned
object
language
type
[
t
]
.
(
It
is
conventional
in
the
world
of
interactive
theorem
proving
to
call
the
language
of
the
proof
assistant
the
%
\
index
{
meta
language
}%
_
meta
language_
and
a
language
being
formalized
the
%
\
index
{
object
language
}%
_
object
language_
.
)
*
)
(
**
We
can
define
a
similar
type
family
for
typed
expressions
,
where
a
term
of
type
[
texp
t
]
can
be
assigned
object
language
type
[
t
]
.
(
It
is
conventional
in
the
world
of
interactive
theorem
proving
to
call
the
language
of
the
proof
assistant
the
%
\
index
{
meta
language
}%
_
meta
language_
and
a
language
being
formalized
the
%
\
index
{
object
language
}%
_
object
language_
.
)
*
)
...
...
src/Universes.v
View file @
a6fa0f0e
...
@@ -238,8 +238,11 @@ Error: Universe inconsistency (cannot enforce Top.42 < Top.42).
...
@@ -238,8 +238,11 @@ Error: Universe inconsistency (cannot enforce Top.42 < Top.42).
We
are
unable
to
instantiate
the
parameter
[
T
]
of
[
Const
]
with
an
[
exp
]
type
.
To
see
why
,
it
is
helpful
to
print
the
annotated
version
of
[
exp
]
'
s
inductive
definition
.
*
)
We
are
unable
to
instantiate
the
parameter
[
T
]
of
[
Const
]
with
an
[
exp
]
type
.
To
see
why
,
it
is
helpful
to
print
the
annotated
version
of
[
exp
]
'
s
inductive
definition
.
*
)
(
**
[[
Print
exp
.
Print
exp
.
(
**
%
\
vspace
{-
.15
in
}%
[[
]]
[[
Inductive
exp
Inductive
exp
:
Type
$
Top
.8
^
->
:
Type
$
Top
.8
^
->
Type
Type
...
@@ -268,8 +271,16 @@ The command outputs many more constraints, but we have collected only those that
...
@@ -268,8 +271,16 @@ The command outputs many more constraints, but we have collected only those that
The
next
constraint
,
for
[
Top
.15
]
,
is
more
complicated
.
This
is
the
universe
of
the
second
argument
to
the
[
Pair
]
constructor
.
Not
only
must
[
Top
.15
]
be
less
than
[
Top
.8
]
,
but
it
also
comes
out
that
[
Top
.8
]
must
be
less
than
[
Coq
.
Init
.
Datatypes
.38
]
.
What
is
this
new
universe
variable
?
It
is
from
the
definition
of
the
[
prod
]
inductive
family
,
to
which
types
of
the
form
[
A
*
B
]
are
desugared
.
*
)
The
next
constraint
,
for
[
Top
.15
]
,
is
more
complicated
.
This
is
the
universe
of
the
second
argument
to
the
[
Pair
]
constructor
.
Not
only
must
[
Top
.15
]
be
less
than
[
Top
.8
]
,
but
it
also
comes
out
that
[
Top
.8
]
must
be
less
than
[
Coq
.
Init
.
Datatypes
.38
]
.
What
is
this
new
universe
variable
?
It
is
from
the
definition
of
the
[
prod
]
inductive
family
,
to
which
types
of
the
form
[
A
*
B
]
are
desugared
.
*
)
(
*
begin
hide
*
)
Inductive
prod
:=
pair
.
Reset
prod
.
(
*
end
hide
*
)
(
**
[[
Print
prod
.
Print
prod
.
(
**
%
\
vspace
{-
.15
in
}%
[[
]]
[[
Inductive
prod
(
A
:
Type
$
Coq
.
Init
.
Datatypes
.37
^
)
Inductive
prod
(
A
:
Type
$
Coq
.
Init
.
Datatypes
.37
^
)
(
B
:
Type
$
Coq
.
Init
.
Datatypes
.38
^
)
(
B
:
Type
$
Coq
.
Init
.
Datatypes
.38
^
)
:
Type
$
max
(
Coq
.
Init
.
Datatypes
.37
,
Coq
.
Init
.
Datatypes
.38
)
^
:=
:
Type
$
max
(
Coq
.
Init
.
Datatypes
.37
,
Coq
.
Init
.
Datatypes
.38
)
^
:=
...
...
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