Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Open sidebar
Wim Lewis
nettle
Commits
a23a6ddc
Commit
a23a6ddc
authored
Apr 17, 2013
by
Niels Möller
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Use mini-gmp for the eccdata program.
parent
ad689768
Changes
5
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
4446 additions
and
12 deletions
+4446
-12
ChangeLog
ChangeLog
+9
-0
Makefile.in
Makefile.in
+1
-1
eccdata.c
eccdata.c
+49
-11
mini-gmp.c
mini-gmp.c
+4131
-0
mini-gmp.h
mini-gmp.h
+256
-0
No files found.
ChangeLog
View file @
a23a6ddc
2013-04-17 Niels Möller <nisse@lysator.liu.se>
* eccdata.c: Use mini-gmp, to avoid gmp dependency and associated
configure tests for the *build* system. Replaced mpz_submul_ui by
mpz_mul_ui + mpz_sub, and gmp_printf and gmp_fprintf by calls to
mpz_out_str.
* mini-gmp.h, mini-gmp.c: New files, copied from gmp-5.1.1.
2013-04-16 Niels Möller <nisse@lysator.liu.se>
* umac-set-key.c (BE_SWAP32_N): Fixed dummy definition used for
...
...
Makefile.in
View file @
a23a6ddc
...
...
@@ -247,7 +247,7 @@ gcmdata$(EXEEXT_FOR_BUILD): gcmdata.c
$(CC_FOR_BUILD)
`
test
-f
gcmdata.c
||
echo
'
$(srcdir)
/'
`
gcmdata.c
\
-o
gcmdata
$(EXEEXT_FOR_BUILD)
eccdata$(EXEEXT_FOR_BUILD)
:
eccdata.c
eccdata$(EXEEXT_FOR_BUILD)
:
eccdata.c
mini-gmp.c mini-gmp.h
$(CC_FOR_BUILD)
`
test
-f
eccdata.c
||
echo
'
$(srcdir)
/'
`
eccdata.c
-lgmp
\
-o
eccdata
$(EXEEXT_FOR_BUILD)
...
...
eccdata.c
View file @
a23a6ddc
...
...
@@ -29,7 +29,7 @@
#include <stdlib.h>
#include <string.h>
#include
<
gmp.
h>
#include
"mini-
gmp.
c"
/* Affine coordinates, for simplicity. Infinity point represented as x
== y == 0. */
...
...
@@ -142,7 +142,9 @@ ecc_dup (const struct ecc_curve *ecc,
/* x' = t^2 - 2 x */
mpz_mul
(
x
,
t
,
t
);
mpz_submul_ui
(
x
,
p
->
x
,
2
);
/* mpz_submul_ui (x, p->x, 2); not available in mini-gmp */
mpz_mul_ui
(
m
,
p
->
x
,
2
);
mpz_sub
(
x
,
x
,
m
);
mpz_mod
(
x
,
x
,
ecc
->
p
);
/* y' = (x - x') * t - y */
...
...
@@ -551,8 +553,15 @@ ecc_mul_pippenger (const struct ecc_curve *ecc,
{ \
fprintf (stderr, "%s:%d: ASSERT_EQUAL (%s, %s) failed.\n", \
__FILE__, __LINE__, #p, #q); \
gmp_fprintf (stderr, "p = (%Zx,\n %Zx)\n", (p)->x, (p)->y); \
gmp_fprintf (stderr, "q = (%Zx,\n %Zx)\n", (q)->x, (q)->y); \
fprintf (stderr, "p = ("); \
mpz_out_str (stderr, 16, (p)->x); \
fprintf (stderr, ",\n "); \
mpz_out_str (stderr, 16, (p)->y); \
fprintf (stderr, ")\nq = ("); \
mpz_out_str (stderr, 16, (q)->x); \
fprintf (stderr, ",\n "); \
mpz_out_str (stderr, 16, (q)->y); \
fprintf (stderr, ")\n"); \
abort(); \
} \
} while (0)
...
...
@@ -562,7 +571,11 @@ ecc_mul_pippenger (const struct ecc_curve *ecc,
{ \
fprintf (stderr, "%s:%d: ASSERT_ZERO (%s) failed.\n", \
__FILE__, __LINE__, #p); \
gmp_fprintf (stderr, "p = (%Zx,\n %Zx)\n", (p)->x, (p)->y); \
fprintf (stderr, "p = ("); \
mpz_out_str (stderr, 16, (p)->x); \
fprintf (stderr, ",\n "); \
mpz_out_str (stderr, 16, (p)->y); \
fprintf (stderr, ")\n"); \
abort(); \
} \
} while (0)
...
...
@@ -581,25 +594,48 @@ ecc_curve_check (const struct ecc_curve *ecc)
if
(
ecc
->
ref
)
ASSERT_EQUAL
(
&
p
,
&
ecc
->
ref
[
0
]);
else
gmp_fprintf
(
stderr
,
"g2 = %Zx
\n
%Zx
\n
"
,
p
.
x
,
p
.
y
);
{
fprintf
(
stderr
,
"g2 = "
);
mpz_out_str
(
stderr
,
16
,
p
.
x
);
fprintf
(
stderr
,
"
\n
"
);
mpz_out_str
(
stderr
,
16
,
p
.
y
);
fprintf
(
stderr
,
"
\n
"
);
}
ecc_add
(
ecc
,
&
q
,
&
p
,
&
ecc
->
g
);
if
(
ecc
->
ref
)
ASSERT_EQUAL
(
&
q
,
&
ecc
->
ref
[
1
]);
else
gmp_fprintf
(
stderr
,
"g3 = %Zx
\n
%Zx
\n
"
,
q
.
x
,
q
.
y
);
{
fprintf
(
stderr
,
"g3 = "
);
mpz_out_str
(
stderr
,
16
,
q
.
x
);
fprintf
(
stderr
,
"
\n
"
);
mpz_out_str
(
stderr
,
16
,
q
.
y
);
fprintf
(
stderr
,
"
\n
"
);
}
ecc_add
(
ecc
,
&
q
,
&
q
,
&
ecc
->
g
);
if
(
ecc
->
ref
)
ASSERT_EQUAL
(
&
q
,
&
ecc
->
ref
[
2
]);
else
gmp_fprintf
(
stderr
,
"g4 = %Zx
\n
%Zx
\n
"
,
q
.
x
,
q
.
y
);
{
fprintf
(
stderr
,
"g4 = "
);
mpz_out_str
(
stderr
,
16
,
q
.
x
);
fprintf
(
stderr
,
"
\n
"
);
mpz_out_str
(
stderr
,
16
,
q
.
y
);
fprintf
(
stderr
,
"
\n
"
);
}
ecc_dup
(
ecc
,
&
q
,
&
p
);
if
(
ecc
->
ref
)
ASSERT_EQUAL
(
&
q
,
&
ecc
->
ref
[
2
]);
else
gmp_fprintf
(
stderr
,
"g4 = %Zx
\n
%Zx
\n
"
,
q
.
x
,
q
.
y
);
{
fprintf
(
stderr
,
"g4 = "
);
mpz_out_str
(
stderr
,
16
,
q
.
x
);
fprintf
(
stderr
,
"
\n
"
);
mpz_out_str
(
stderr
,
16
,
q
.
y
);
fprintf
(
stderr
,
"
\n
"
);
}
ecc_mul_binary
(
ecc
,
&
p
,
ecc
->
q
,
&
ecc
->
g
);
ASSERT_ZERO
(
&
p
);
...
...
@@ -639,7 +675,9 @@ output_digits (const mpz_t x,
printf
(
"
\n
"
);
mpz_and
(
limb
,
mask
,
t
);
gmp_printf
(
" 0x%Zx%s,"
,
limb
,
suffix
);
printf
(
" 0x"
);
mpz_out_str
(
stdout
,
16
,
limb
);
printf
(
"%s,"
,
suffix
);
mpz_tdiv_q_2exp
(
t
,
t
,
bits_per_limb
);
}
...
...
mini-gmp.c
0 → 100644
View file @
a23a6ddc
This diff is collapsed.
Click to expand it.
mini-gmp.h
0 → 100644
View file @
a23a6ddc
/* mini-gmp, a minimalistic implementation of a GNU GMP subset.
Copyright 2011, 2012, 2013 Free Software Foundation, Inc.
This file is part of the GNU MP Library.
The GNU MP Library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 of the License, or (at your
option) any later version.
The GNU MP Library 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 Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the GNU MP Library. If not, see http://www.gnu.org/licenses/. */
/* About mini-gmp: This is a minimal implementation of a subset of the
GMP interface. It is intended for inclusion into applications which
have modest bignums needs, as a fallback when the real GMP library
is not installed.
This file defines the public interface. */
#ifndef __MINI_GMP_H__
#define __MINI_GMP_H__
/* For size_t */
#include <stddef.h>
#if defined (__cplusplus)
extern
"C"
{
#endif
void
mp_set_memory_functions
(
void
*
(
*
)
(
size_t
),
void
*
(
*
)
(
void
*
,
size_t
,
size_t
),
void
(
*
)
(
void
*
,
size_t
));
void
mp_get_memory_functions
(
void
*
(
**
)
(
size_t
),
void
*
(
**
)
(
void
*
,
size_t
,
size_t
),
void
(
**
)
(
void
*
,
size_t
));
typedef
unsigned
long
mp_limb_t
;
typedef
long
mp_size_t
;
typedef
unsigned
long
mp_bitcnt_t
;
typedef
mp_limb_t
*
mp_ptr
;
typedef
const
mp_limb_t
*
mp_srcptr
;
typedef
struct
{
int
_mp_alloc
;
/* Number of *limbs* allocated and pointed
to by the _mp_d field. */
int
_mp_size
;
/* abs(_mp_size) is the number of limbs the
last field points to. If _mp_size is
negative this is a negative number. */
mp_limb_t
*
_mp_d
;
/* Pointer to the limbs. */
}
__mpz_struct
;
typedef
__mpz_struct
mpz_t
[
1
];
typedef
__mpz_struct
*
mpz_ptr
;
typedef
const
__mpz_struct
*
mpz_srcptr
;
void
mpn_copyi
(
mp_ptr
,
mp_srcptr
,
mp_size_t
);
void
mpn_copyd
(
mp_ptr
,
mp_srcptr
,
mp_size_t
);
int
mpn_cmp
(
mp_srcptr
,
mp_srcptr
,
mp_size_t
);
mp_limb_t
mpn_add_1
(
mp_ptr
,
mp_srcptr
,
mp_size_t
,
mp_limb_t
);
mp_limb_t
mpn_add_n
(
mp_ptr
,
mp_srcptr
,
mp_srcptr
,
mp_size_t
);
mp_limb_t
mpn_add
(
mp_ptr
,
mp_srcptr
,
mp_size_t
,
mp_srcptr
,
mp_size_t
);
mp_limb_t
mpn_sub_1
(
mp_ptr
,
mp_srcptr
,
mp_size_t
,
mp_limb_t
);
mp_limb_t
mpn_sub_n
(
mp_ptr
,
mp_srcptr
,
mp_srcptr
,
mp_size_t
);
mp_limb_t
mpn_sub
(
mp_ptr
,
mp_srcptr
,
mp_size_t
,
mp_srcptr
,
mp_size_t
);
mp_limb_t
mpn_mul_1
(
mp_ptr
,
mp_srcptr
,
mp_size_t
,
mp_limb_t
);
mp_limb_t
mpn_addmul_1
(
mp_ptr
,
mp_srcptr
,
mp_size_t
,
mp_limb_t
);
mp_limb_t
mpn_submul_1
(
mp_ptr
,
mp_srcptr
,
mp_size_t
,
mp_limb_t
);
mp_limb_t
mpn_mul
(
mp_ptr
,
mp_srcptr
,
mp_size_t
,
mp_srcptr
,
mp_size_t
);
void
mpn_mul_n
(
mp_ptr
,
mp_srcptr
,
mp_srcptr
,
mp_size_t
);
void
mpn_sqr
(
mp_ptr
,
mp_srcptr
,
mp_size_t
);
mp_limb_t
mpn_lshift
(
mp_ptr
,
mp_srcptr
,
mp_size_t
,
unsigned
int
);
mp_limb_t
mpn_rshift
(
mp_ptr
,
mp_srcptr
,
mp_size_t
,
unsigned
int
);
mp_limb_t
mpn_invert_3by2
(
mp_limb_t
,
mp_limb_t
);
#define mpn_invert_limb(x) mpn_invert_3by2 ((x), 0)
size_t
mpn_get_str
(
unsigned
char
*
,
int
,
mp_ptr
,
mp_size_t
);
mp_size_t
mpn_set_str
(
mp_ptr
,
const
unsigned
char
*
,
size_t
,
int
);
void
mpz_init
(
mpz_t
);
void
mpz_init2
(
mpz_t
,
mp_bitcnt_t
);
void
mpz_clear
(
mpz_t
);
#define mpz_odd_p(z) (((z)->_mp_size != 0) & (int) (z)->_mp_d[0])
#define mpz_even_p(z) (! mpz_odd_p (z))
int
mpz_sgn
(
const
mpz_t
);
int
mpz_cmp_si
(
const
mpz_t
,
long
);
int
mpz_cmp_ui
(
const
mpz_t
,
unsigned
long
);
int
mpz_cmp
(
const
mpz_t
,
const
mpz_t
);
int
mpz_cmpabs_ui
(
const
mpz_t
,
unsigned
long
);
int
mpz_cmpabs
(
const
mpz_t
,
const
mpz_t
);
int
mpz_cmp_d
(
const
mpz_t
,
double
);
int
mpz_cmpabs_d
(
const
mpz_t
,
double
);
void
mpz_abs
(
mpz_t
,
const
mpz_t
);
void
mpz_neg
(
mpz_t
,
const
mpz_t
);
void
mpz_swap
(
mpz_t
,
mpz_t
);
void
mpz_add_ui
(
mpz_t
,
const
mpz_t
,
unsigned
long
);
void
mpz_add
(
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_sub_ui
(
mpz_t
,
const
mpz_t
,
unsigned
long
);
void
mpz_ui_sub
(
mpz_t
,
unsigned
long
,
const
mpz_t
);
void
mpz_sub
(
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_mul_si
(
mpz_t
,
const
mpz_t
,
long
int
);
void
mpz_mul_ui
(
mpz_t
,
const
mpz_t
,
unsigned
long
int
);
void
mpz_mul
(
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_mul_2exp
(
mpz_t
,
const
mpz_t
,
mp_bitcnt_t
);
void
mpz_cdiv_qr
(
mpz_t
,
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_fdiv_qr
(
mpz_t
,
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_tdiv_qr
(
mpz_t
,
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_cdiv_q
(
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_fdiv_q
(
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_tdiv_q
(
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_cdiv_r
(
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_fdiv_r
(
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_tdiv_r
(
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_cdiv_q_2exp
(
mpz_t
,
const
mpz_t
,
mp_bitcnt_t
);
void
mpz_fdiv_q_2exp
(
mpz_t
,
const
mpz_t
,
mp_bitcnt_t
);
void
mpz_tdiv_q_2exp
(
mpz_t
,
const
mpz_t
,
mp_bitcnt_t
);
void
mpz_cdiv_r_2exp
(
mpz_t
,
const
mpz_t
,
mp_bitcnt_t
);
void
mpz_fdiv_r_2exp
(
mpz_t
,
const
mpz_t
,
mp_bitcnt_t
);
void
mpz_tdiv_r_2exp
(
mpz_t
,
const
mpz_t
,
mp_bitcnt_t
);
void
mpz_mod
(
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_divexact
(
mpz_t
,
const
mpz_t
,
const
mpz_t
);
int
mpz_divisible_p
(
const
mpz_t
,
const
mpz_t
);
unsigned
long
mpz_cdiv_qr_ui
(
mpz_t
,
mpz_t
,
const
mpz_t
,
unsigned
long
);
unsigned
long
mpz_fdiv_qr_ui
(
mpz_t
,
mpz_t
,
const
mpz_t
,
unsigned
long
);
unsigned
long
mpz_tdiv_qr_ui
(
mpz_t
,
mpz_t
,
const
mpz_t
,
unsigned
long
);
unsigned
long
mpz_cdiv_q_ui
(
mpz_t
,
const
mpz_t
,
unsigned
long
);
unsigned
long
mpz_fdiv_q_ui
(
mpz_t
,
const
mpz_t
,
unsigned
long
);
unsigned
long
mpz_tdiv_q_ui
(
mpz_t
,
const
mpz_t
,
unsigned
long
);
unsigned
long
mpz_cdiv_r_ui
(
mpz_t
,
const
mpz_t
,
unsigned
long
);
unsigned
long
mpz_fdiv_r_ui
(
mpz_t
,
const
mpz_t
,
unsigned
long
);
unsigned
long
mpz_tdiv_r_ui
(
mpz_t
,
const
mpz_t
,
unsigned
long
);
unsigned
long
mpz_cdiv_ui
(
const
mpz_t
,
unsigned
long
);
unsigned
long
mpz_fdiv_ui
(
const
mpz_t
,
unsigned
long
);
unsigned
long
mpz_tdiv_ui
(
const
mpz_t
,
unsigned
long
);
unsigned
long
mpz_mod_ui
(
mpz_t
,
const
mpz_t
,
unsigned
long
);
void
mpz_divexact_ui
(
mpz_t
,
const
mpz_t
,
unsigned
long
);
int
mpz_divisible_ui_p
(
const
mpz_t
,
unsigned
long
);
unsigned
long
mpz_gcd_ui
(
mpz_t
,
const
mpz_t
,
unsigned
long
);
void
mpz_gcd
(
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_gcdext
(
mpz_t
,
mpz_t
,
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_lcm_ui
(
mpz_t
,
const
mpz_t
,
unsigned
long
);
void
mpz_lcm
(
mpz_t
,
const
mpz_t
,
const
mpz_t
);
int
mpz_invert
(
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_sqrtrem
(
mpz_t
,
mpz_t
,
const
mpz_t
);
void
mpz_sqrt
(
mpz_t
,
const
mpz_t
);
void
mpz_pow_ui
(
mpz_t
,
const
mpz_t
,
unsigned
long
);
void
mpz_ui_pow_ui
(
mpz_t
,
unsigned
long
,
unsigned
long
);
void
mpz_powm
(
mpz_t
,
const
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_powm_ui
(
mpz_t
,
const
mpz_t
,
unsigned
long
,
const
mpz_t
);
void
mpz_rootrem
(
mpz_t
,
mpz_t
,
const
mpz_t
,
unsigned
long
);
int
mpz_root
(
mpz_t
,
const
mpz_t
,
unsigned
long
);
void
mpz_fac_ui
(
mpz_t
,
unsigned
long
);
void
mpz_bin_uiui
(
mpz_t
,
unsigned
long
,
unsigned
long
);
int
mpz_tstbit
(
const
mpz_t
,
mp_bitcnt_t
);
void
mpz_setbit
(
mpz_t
,
mp_bitcnt_t
);
void
mpz_clrbit
(
mpz_t
,
mp_bitcnt_t
);
void
mpz_combit
(
mpz_t
,
mp_bitcnt_t
);
void
mpz_com
(
mpz_t
,
const
mpz_t
);
void
mpz_and
(
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_ior
(
mpz_t
,
const
mpz_t
,
const
mpz_t
);
void
mpz_xor
(
mpz_t
,
const
mpz_t
,
const
mpz_t
);
mp_bitcnt_t
mpz_popcount
(
const
mpz_t
);
mp_bitcnt_t
mpz_hamdist
(
const
mpz_t
,
const
mpz_t
);
mp_bitcnt_t
mpz_scan0
(
const
mpz_t
,
mp_bitcnt_t
);
mp_bitcnt_t
mpz_scan1
(
const
mpz_t
,
mp_bitcnt_t
);
int
mpz_fits_slong_p
(
const
mpz_t
);
int
mpz_fits_ulong_p
(
const
mpz_t
);
long
int
mpz_get_si
(
const
mpz_t
);
unsigned
long
int
mpz_get_ui
(
const
mpz_t
);
double
mpz_get_d
(
const
mpz_t
);
size_t
mpz_size
(
const
mpz_t
);
mp_limb_t
mpz_getlimbn
(
const
mpz_t
,
mp_size_t
);
void
mpz_set_si
(
mpz_t
,
signed
long
int
);
void
mpz_set_ui
(
mpz_t
,
unsigned
long
int
);
void
mpz_set
(
mpz_t
,
const
mpz_t
);
void
mpz_set_d
(
mpz_t
,
double
);
void
mpz_init_set_si
(
mpz_t
,
signed
long
int
);
void
mpz_init_set_ui
(
mpz_t
,
unsigned
long
int
);
void
mpz_init_set
(
mpz_t
,
const
mpz_t
);
void
mpz_init_set_d
(
mpz_t
,
double
);
size_t
mpz_sizeinbase
(
const
mpz_t
,
int
);
char
*
mpz_get_str
(
char
*
,
int
,
const
mpz_t
);
int
mpz_set_str
(
mpz_t
,
const
char
*
,
int
);
int
mpz_init_set_str
(
mpz_t
,
const
char
*
,
int
);
/* This long list taken from gmp.h. */
/* For reference, "defined(EOF)" cannot be used here. In g++ 2.95.4,
<iostream> defines EOF but not FILE. */
#if defined (FILE) \
|| defined (H_STDIO) \
|| defined (_H_STDIO)
/* AIX */
\
|| defined (_STDIO_H)
/* glibc, Sun, SCO */
\
|| defined (_STDIO_H_)
/* BSD, OSF */
\
|| defined (__STDIO_H)
/* Borland */
\
|| defined (__STDIO_H__)
/* IRIX */
\
|| defined (_STDIO_INCLUDED)
/* HPUX */
\
|| defined (__dj_include_stdio_h_)
/* DJGPP */
\
|| defined (_FILE_DEFINED)
/* Microsoft */
\
|| defined (__STDIO__)
/* Apple MPW MrC */
\
|| defined (_MSL_STDIO_H)
/* Metrowerks */
\
|| defined (_STDIO_H_INCLUDED)
/* QNX4 */
\
|| defined (_ISO_STDIO_ISO_H)
/* Sun C++ */
\
|| defined (__STDIO_LOADED)
/* VMS */
size_t
mpz_out_str
(
FILE
*
,
int
,
const
mpz_t
);
#endif
void
mpz_import
(
mpz_t
,
size_t
,
int
,
size_t
,
int
,
size_t
,
const
void
*
);
void
*
mpz_export
(
void
*
,
size_t
*
,
int
,
size_t
,
int
,
size_t
,
const
mpz_t
);
#if defined (__cplusplus)
}
#endif
#endif
/* __MINI_GMP_H__ */
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