Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Xiph.Org
aom-rav1e
Commits
cfc5ac50
Commit
cfc5ac50
authored
Oct 19, 2016
by
Yaowu Xu
Committed by
Gerrit Code Review
Oct 19, 2016
Browse files
Merge "Partition the ans experiment into 'ans' and 'rans'" into nextgenv2
parents
f693610a
ec6fb649
Changes
11
Hide whitespace changes
Inline
Side-by-side
aom_dsp/bitreader.h
View file @
cfc5ac50
...
...
@@ -206,7 +206,7 @@ static INLINE int aom_read_tree_(aom_reader *r, const aom_tree_index *tree,
static
INLINE
int
aom_read_symbol_
(
aom_reader
*
r
,
const
aom_cdf_prob
*
cdf
,
int
nsymbs
ACCT_STR_PARAM
)
{
int
ret
;
#if CONFIG_ANS
#if CONFIG_
R
ANS
(
void
)
nsymbs
;
ret
=
rans_read
(
r
,
cdf
);
#elif CONFIG_DAALA_EC
...
...
aom_dsp/bitwriter.h
View file @
cfc5ac50
...
...
@@ -100,7 +100,7 @@ static INLINE void aom_write_tree(aom_writer *w, const aom_tree_index *tree,
static
INLINE
void
aom_write_symbol
(
aom_writer
*
w
,
int
symb
,
const
aom_cdf_prob
*
cdf
,
int
nsymbs
)
{
#if CONFIG_ANS
#if CONFIG_
R
ANS
struct
rans_sym
s
;
(
void
)
nsymbs
;
assert
(
cdf
);
...
...
av1/common/entropy.c
View file @
cfc5ac50
...
...
@@ -406,16 +406,18 @@ const aom_prob av1_pareto8_full[COEFF_PROB_MODELS][MODEL_NODES] = {
{
255
,
246
,
247
,
255
,
239
,
255
,
253
,
255
},
};
#if CONFIG_ANS
// Model obtained from a 2-sided zero-centerd distribu
i
tion derived
#if CONFIG_
R
ANS
// Model obtained from a 2-sided zero-center
e
d distribution derived
// from a Pareto distribution. The cdf of the distribution is:
// cdf(x) = 0.5 + 0.5 * sgn(x) * [1 - {alpha/(alpha + |x|)} ^ beta]
//
// For a given beta and a given probablity of the 1-node, the alpha
// For a given beta and a given probab
i
lity of the 1-node, the alpha
// is first solved, and then the {alpha, beta} pair is used to generate
// the probabilities for the rest of the nodes.
//
// beta = 8
// The full source code of the generating program is available in:
// tools/gen_constrained_tokenset.py
//
// Values for tokens ONE_TOKEN through CATEGORY6_TOKEN included here.
// ZERO_TOKEN and EOB_TOKEN are coded as flags outside this coder.
const
aom_cdf_prob
...
...
@@ -676,7 +678,7 @@ const aom_cdf_prob
{
32512
,
238
,
11
,
1
,
1
,
1
,
1
,
1
,
1
,
1
},
{
32640
,
117
,
4
,
1
,
1
,
1
,
1
,
1
,
1
,
1
},
};
#endif // CONFIG_ANS
#endif // CONFIG_
R
ANS
/* clang-format off */
#if CONFIG_ENTROPY
...
...
@@ -2801,7 +2803,7 @@ void av1_model_to_full_probs(const aom_prob *model, aom_prob *full) {
extend_to_full_distribution
(
&
full
[
UNCONSTRAINED_NODES
],
model
[
PIVOT_NODE
]);
}
#if CONFIG_ANS
#if CONFIG_
R
ANS
static
void
build_token_cdfs
(
const
aom_prob
*
pdf_model
,
aom_cdf_prob
cdf
[
ENTROPY_TOKENS
])
{
int
i
,
sum
=
0
;
...
...
@@ -2818,12 +2820,11 @@ void av1_coef_pareto_cdfs(FRAME_CONTEXT *fc) {
for
(
i
=
0
;
i
<
PLANE_TYPES
;
++
i
)
for
(
j
=
0
;
j
<
REF_TYPES
;
++
j
)
for
(
k
=
0
;
k
<
COEF_BANDS
;
++
k
)
for
(
l
=
0
;
l
<
BAND_COEFF_CONTEXTS
(
k
);
++
l
)
{
for
(
l
=
0
;
l
<
BAND_COEFF_CONTEXTS
(
k
);
++
l
)
build_token_cdfs
(
fc
->
coef_probs
[
t
][
i
][
j
][
k
][
l
],
fc
->
coef_cdfs
[
t
][
i
][
j
][
k
][
l
]);
}
}
#endif // CONFIG_ANS
#endif // CONFIG_
R
ANS
void
av1_default_coef_probs
(
AV1_COMMON
*
cm
)
{
#if CONFIG_ENTROPY
...
...
@@ -2836,9 +2837,9 @@ void av1_default_coef_probs(AV1_COMMON *cm) {
av1_copy
(
cm
->
fc
->
coef_probs
[
TX_16X16
],
default_coef_probs_16x16
);
av1_copy
(
cm
->
fc
->
coef_probs
[
TX_32X32
],
default_coef_probs_32x32
);
#endif // CONFIG_ENTROPY
#if CONFIG_ANS
#if CONFIG_
R
ANS
av1_coef_pareto_cdfs
(
cm
->
fc
);
#endif // CONFIG_ANS
#endif // CONFIG_
R
ANS
}
static
void
adapt_coef_probs
(
AV1_COMMON
*
cm
,
TX_SIZE
tx_size
,
...
...
@@ -2905,9 +2906,9 @@ void av1_adapt_coef_probs(AV1_COMMON *cm) {
#endif // CONFIG_ENTROPY
for
(
t
=
TX_4X4
;
t
<=
TX_32X32
;
t
++
)
adapt_coef_probs
(
cm
,
t
,
count_sat
,
update_factor
);
#if CONFIG_ANS
#if CONFIG_
R
ANS
av1_coef_pareto_cdfs
(
cm
->
fc
);
#endif
#endif
// CONFIG_RANS
}
#if CONFIG_ENTROPY
...
...
av1/common/entropy.h
View file @
cfc5ac50
...
...
@@ -12,12 +12,13 @@
#ifndef AV1_COMMON_ENTROPY_H_
#define AV1_COMMON_ENTROPY_H_
#include
"./aom_config.h"
#include
"aom/aom_integer.h"
#if CONFIG_RANS
#include
"aom_dsp/ans.h"
#endif // CONFIG_RANS
#include
"aom_dsp/prob.h"
#if CONFIG_ANS
#include
"aom_dsp/ans.h"
#endif // CONFIG_ANS
#include
"av1/common/common.h"
#include
"av1/common/enums.h"
...
...
@@ -190,12 +191,12 @@ static INLINE const uint8_t *get_band_translate(TX_SIZE tx_size) {
#define MODEL_NODES (ENTROPY_NODES - UNCONSTRAINED_NODES)
extern
const
aom_tree_index
av1_coef_con_tree
[
TREE_SIZE
(
ENTROPY_TOKENS
)];
extern
const
aom_prob
av1_pareto8_full
[
COEFF_PROB_MODELS
][
MODEL_NODES
];
#if CONFIG_ANS || CONFIG_DAALA_EC
#if CONFIG_
R
ANS || CONFIG_DAALA_EC
typedef
aom_cdf_prob
coeff_cdf_model
[
REF_TYPES
][
COEF_BANDS
][
COEFF_CONTEXTS
]
[
ENTROPY_TOKENS
];
extern
const
aom_cdf_prob
av1_pareto8_token_probs
[
COEFF_PROB_MODELS
]
[
ENTROPY_TOKENS
-
2
];
#endif // CONFIG_ANS
#endif // CONFIG_
R
ANS
typedef
aom_prob
av1_coeff_probs_model
[
REF_TYPES
][
COEF_BANDS
][
COEFF_CONTEXTS
]
[
UNCONSTRAINED_NODES
];
...
...
@@ -265,10 +266,10 @@ static INLINE int get_entropy_context(TX_SIZE tx_size, const ENTROPY_CONTEXT *a,
return
combine_entropy_contexts
(
above_ec
,
left_ec
);
}
#if CONFIG_ANS
#if CONFIG_
R
ANS
struct
frame_contexts
;
void
av1_coef_pareto_cdfs
(
struct
frame_contexts
*
fc
);
#endif // CONFIG_ANS
#endif // CONFIG_
R
ANS
#if CONFIG_ENTROPY
#define COEF_COUNT_SAT_BITS 5
...
...
av1/common/entropymode.h
View file @
cfc5ac50
...
...
@@ -56,9 +56,9 @@ typedef struct frame_contexts {
aom_prob
partition_prob
[
PARTITION_CONTEXTS
][
PARTITION_TYPES
-
1
];
#endif
av1_coeff_probs_model
coef_probs
[
TX_SIZES
][
PLANE_TYPES
];
#if CONFIG_ANS || CONFIG_DAALA_EC
#if CONFIG_
R
ANS || CONFIG_DAALA_EC
coeff_cdf_model
coef_cdfs
[
TX_SIZES
][
PLANE_TYPES
];
#endif
#endif
// CONFIG_RANS
aom_prob
switchable_interp_prob
[
SWITCHABLE_FILTER_CONTEXTS
]
[
SWITCHABLE_FILTERS
-
1
];
...
...
av1/decoder/decodeframe.c
View file @
cfc5ac50
...
...
@@ -1930,9 +1930,9 @@ static void read_coef_probs(FRAME_CONTEXT *fc, TX_MODE tx_mode, aom_reader *r) {
TX_SIZE
tx_size
;
for
(
tx_size
=
TX_4X4
;
tx_size
<=
max_tx_size
;
++
tx_size
)
read_coef_probs_common
(
fc
->
coef_probs
[
tx_size
],
r
);
#if CONFIG_ANS
#if CONFIG_
R
ANS
av1_coef_pareto_cdfs
(
fc
);
#endif // CONFIG_ANS
#endif // CONFIG_
R
ANS
}
static
void
setup_segmentation
(
AV1_COMMON
*
const
cm
,
...
...
av1/decoder/detokenize.c
View file @
cfc5ac50
...
...
@@ -76,11 +76,11 @@ static int decode_coefs(const MACROBLOCKD *xd, PLANE_TYPE type,
const
aom_prob
(
*
coef_probs
)[
COEFF_CONTEXTS
][
UNCONSTRAINED_NODES
]
=
fc
->
coef_probs
[
tx_size_ctx
][
type
][
ref
];
const
aom_prob
*
prob
;
#if CONFIG_ANS
#if CONFIG_
R
ANS
const
aom_cdf_prob
(
*
const
coef_cdfs
)[
COEFF_CONTEXTS
][
ENTROPY_TOKENS
]
=
fc
->
coef_cdfs
[
tx_size_ctx
][
type
][
ref
];
const
aom_cdf_prob
(
*
cdf
)[
ENTROPY_TOKENS
];
#endif // CONFIG_ANS
#endif // CONFIG_
R
ANS
unsigned
int
(
*
coef_counts
)[
COEFF_CONTEXTS
][
UNCONSTRAINED_NODES
+
1
];
unsigned
int
(
*
eob_branch_count
)[
COEFF_CONTEXTS
];
uint8_t
token_cache
[
MAX_TX_SQUARE
];
...
...
@@ -166,7 +166,7 @@ static int decode_coefs(const MACROBLOCKD *xd, PLANE_TYPE type,
dqv_val
=
&
dq_val
[
band
][
0
];
#endif // CONFIG_NEW_QUANT
}
#if CONFIG_ANS
#if CONFIG_
R
ANS
cdf
=
&
coef_cdfs
[
band
][
ctx
];
token
=
ONE_TOKEN
+
aom_read_symbol
(
r
,
*
cdf
,
CATEGORY6_TOKEN
-
ONE_TOKEN
+
1
,
ACCT_STR
);
...
...
@@ -263,7 +263,7 @@ static int decode_coefs(const MACROBLOCKD *xd, PLANE_TYPE type,
}
}
}
#endif // CONFIG_ANS
#endif // CONFIG_
R
ANS
#if CONFIG_NEW_QUANT
v
=
av1_dequant_abscoeff_nuq
(
val
,
dqv
,
dqv_val
);
v
=
dq_shift
?
ROUND_POWER_OF_TWO
(
v
,
dq_shift
)
:
v
;
...
...
@@ -368,13 +368,7 @@ void av1_decode_palette_tokens(MACROBLOCKD *const xd, int plane,
int
av1_decode_block_tokens
(
MACROBLOCKD
*
const
xd
,
int
plane
,
const
SCAN_ORDER
*
sc
,
int
x
,
int
y
,
TX_SIZE
tx_size
,
TX_TYPE
tx_type
,
#if CONFIG_ANS
struct
AnsDecoder
*
const
r
,
#else
aom_reader
*
r
,
#endif // CONFIG_ANS
int
seg_id
)
{
TX_TYPE
tx_type
,
aom_reader
*
r
,
int
seg_id
)
{
struct
macroblockd_plane
*
const
pd
=
&
xd
->
plane
[
plane
];
const
int16_t
*
const
dequant
=
pd
->
seg_dequant
[
seg_id
];
const
int
ctx
=
...
...
av1/encoder/bitstream.c
View file @
cfc5ac50
...
...
@@ -702,11 +702,11 @@ static void pack_mb_tokens(aom_writer *w, const TOKENEXTRA **tp,
while
(
p
<
stop
&&
p
->
token
!=
EOSB_TOKEN
)
{
const
int
token
=
p
->
token
;
aom_tree_index
index
=
0
;
#if !CONFIG_ANS
#if !CONFIG_
R
ANS
const
struct
av1_token
*
const
coef_encoding
=
&
av1_coef_encodings
[
token
];
int
coef_value
=
coef_encoding
->
value
;
int
coef_length
=
coef_encoding
->
len
;
#endif // !CONFIG_ANS
#endif // !CONFIG_
R
ANS
#if CONFIG_AOM_HIGHBITDEPTH
const
av1_extra_bit
*
const
extra_bits_av1
=
(
bit_depth
==
AOM_BITS_12
)
...
...
@@ -718,7 +718,7 @@ static void pack_mb_tokens(aom_writer *w, const TOKENEXTRA **tp,
(
void
)
bit_depth
;
#endif // CONFIG_AOM_HIGHBITDEPTH
#if CONFIG_ANS
#if CONFIG_
R
ANS
/* skip one or two nodes */
if
(
!
p
->
skip_eob_node
)
aom_write
(
w
,
token
!=
EOB_TOKEN
,
p
->
context_tree
[
0
]);
...
...
@@ -751,7 +751,7 @@ static void pack_mb_tokens(aom_writer *w, const TOKENEXTRA **tp,
}
}
}
#endif // CONFIG_ANS
#endif // CONFIG_
R
ANS
if
(
extra_bits_av1
->
base_val
)
{
const
int
extra_bits
=
p
->
extra
;
...
...
@@ -1661,7 +1661,7 @@ static void write_mb_modes_kf(const AV1_COMMON *cm, const MACROBLOCKD *xd,
#define write_modes_b_wrapper(cpi, tile, w, tok, tok_end, supertx_enabled, \
mi_row, mi_col) \
write_modes_b(cpi, tile, w, tok, tok_end, mi_row, mi_col)
#endif //
CONFIG_ANS &&
CONFIG_SUPERTX
#endif // CONFIG_SUPERTX
static
void
write_modes_b
(
AV1_COMP
*
cpi
,
const
TileInfo
*
const
tile
,
aom_writer
*
w
,
const
TOKENEXTRA
**
tok
,
...
...
@@ -1675,11 +1675,11 @@ static void write_modes_b(AV1_COMP *cpi, const TileInfo *const tile,
MODE_INFO
*
m
;
int
plane
;
int
bh
,
bw
;
#if CONFIG_ANS
#if CONFIG_
R
ANS
(
void
)
tok
;
(
void
)
tok_end
;
(
void
)
plane
;
#endif // !CONFIG_ANS
#endif // !CONFIG_
R
ANS
xd
->
mi
=
cm
->
mi_grid_visible
+
(
mi_row
*
cm
->
mi_stride
+
mi_col
);
m
=
xd
->
mi
[
0
];
...
...
@@ -1854,7 +1854,7 @@ static void write_partition(const AV1_COMMON *const cm,
#define write_modes_sb_wrapper(cpi, tile, w, tok, tok_end, supertx_enabled, \
mi_row, mi_col, bsize) \
write_modes_sb(cpi, tile, w, tok, tok_end, mi_row, mi_col, bsize)
#endif //
CONFIG_ANS &&
CONFIG_SUPERTX
#endif // CONFIG_SUPERTX
static
void
write_modes_sb
(
AV1_COMP
*
const
cpi
,
const
TileInfo
*
const
tile
,
aom_writer
*
const
w
,
const
TOKENEXTRA
**
tok
,
...
...
@@ -2471,9 +2471,9 @@ static void update_coef_probs(AV1_COMP *cpi, aom_writer *w) {
const
TX_MODE
tx_mode
=
cpi
->
common
.
tx_mode
;
const
TX_SIZE
max_tx_size
=
tx_mode_to_biggest_tx_size
[
tx_mode
];
TX_SIZE
tx_size
;
#if CONFIG_ANS
#if CONFIG_
R
ANS
int
update
=
0
;
#endif // CONFIG_ANS
#endif // CONFIG_
R
ANS
#if CONFIG_ENTROPY
AV1_COMMON
*
cm
=
&
cpi
->
common
;
SUBFRAME_STATS
*
subframe_stats
=
&
cpi
->
subframe_stats
;
...
...
@@ -2521,18 +2521,18 @@ static void update_coef_probs(AV1_COMP *cpi, aom_writer *w) {
update_coef_probs_subframe
(
w
,
cpi
,
tx_size
,
cpi
->
branch_ct_buf
,
frame_coef_probs
);
#if CONFIG_ANS
#if CONFIG_
R
ANS
update
=
1
;
#endif // CONFIG_ANS
#endif // CONFIG_
R
ANS
}
else
{
#endif // CONFIG_ENTROPY
build_tree_distribution
(
cpi
,
tx_size
,
frame_branch_ct
,
frame_coef_probs
);
update_coef_probs_common
(
w
,
cpi
,
tx_size
,
frame_branch_ct
,
frame_coef_probs
);
#if CONFIG_ANS
#if CONFIG_
R
ANS
update
=
1
;
#endif // CONFIG_ANS
#endif // CONFIG_
R
ANS
#if CONFIG_ENTROPY
}
#endif // CONFIG_ENTROPY
...
...
@@ -2557,9 +2557,9 @@ static void update_coef_probs(AV1_COMP *cpi, aom_writer *w) {
av1_copy
(
cm
->
counts
.
eob_branch
,
eob_counts_copy
);
}
#endif // CONFIG_ENTROPY
#if CONFIG_ANS
#if CONFIG_
R
ANS
if
(
update
)
av1_coef_pareto_cdfs
(
cpi
->
common
.
fc
);
#endif // CONFIG_ANS
#endif // CONFIG_
R
ANS
}
#if CONFIG_LOOP_RESTORATION
...
...
av1/encoder/tokenize.c
View file @
cfc5ac50
...
...
@@ -336,12 +336,12 @@ const av1_extra_bit av1_extra_bits_high12[ENTROPY_TOKENS] = {
};
#endif
#if !CONFIG_ANS
#if !CONFIG_
R
ANS
const
struct
av1_token
av1_coef_encodings
[
ENTROPY_TOKENS
]
=
{
{
2
,
2
},
{
6
,
3
},
{
28
,
5
},
{
58
,
6
},
{
59
,
6
},
{
60
,
6
},
{
61
,
6
},
{
124
,
7
},
{
125
,
7
},
{
126
,
7
},
{
127
,
7
},
{
0
,
1
}
};
#endif // !CONFIG_ANS
#endif // !CONFIG_
R
ANS
struct
tokenize_b_args
{
const
AV1_COMP
*
cpi
;
...
...
@@ -386,17 +386,17 @@ static void set_entropy_context_b(int plane, int block, int blk_row,
}
static
INLINE
void
add_token
(
TOKENEXTRA
**
t
,
const
aom_prob
*
context_tree
,
#if CONFIG_ANS || CONFIG_DAALA_EC
#if CONFIG_
R
ANS || CONFIG_DAALA_EC
const
aom_cdf_prob
(
*
token_cdf
)[
ENTROPY_TOKENS
],
#endif // CONFIG_ANS
#endif // CONFIG_
R
ANS
int32_t
extra
,
uint8_t
token
,
uint8_t
skip_eob_node
,
unsigned
int
*
counts
)
{
(
*
t
)
->
token
=
token
;
(
*
t
)
->
extra
=
extra
;
(
*
t
)
->
context_tree
=
context_tree
;
#if CONFIG_ANS || CONFIG_DAALA_EC
#if CONFIG_
R
ANS || CONFIG_DAALA_EC
(
*
t
)
->
token_cdf
=
token_cdf
;
#endif // CONFIG_ANS
#endif // CONFIG_
R
ANS
(
*
t
)
->
skip_eob_node
=
skip_eob_node
;
(
*
t
)
++
;
++
counts
[
token
];
...
...
@@ -489,10 +489,10 @@ static void tokenize_b(int plane, int block, int blk_row, int blk_col,
aom_prob
(
*
const
coef_probs
)[
COEFF_CONTEXTS
][
UNCONSTRAINED_NODES
]
=
cpi
->
common
.
fc
->
coef_probs
[
txsize_sqr_map
[
tx_size
]][
type
][
ref
];
#endif // CONFIG_ENTROPY
#if CONFIG_ANS || CONFIG_DAALA_EC
#if CONFIG_
R
ANS || CONFIG_DAALA_EC
aom_cdf_prob
(
*
const
coef_cdfs
)[
COEFF_CONTEXTS
][
ENTROPY_TOKENS
]
=
cpi
->
common
.
fc
->
coef_cdfs
[
tx_size
][
type
][
ref
];
#endif
// CONFIG_ANS
#endif
unsigned
int
(
*
const
eob_branch
)[
COEFF_CONTEXTS
]
=
td
->
counts
->
eob_branch
[
txsize_sqr_map
[
tx_size
]][
type
][
ref
];
const
uint8_t
*
const
band
=
get_band_translate
(
tx_size
);
...
...
@@ -513,9 +513,9 @@ static void tokenize_b(int plane, int block, int blk_row, int blk_col,
av1_get_token_extra
(
v
,
&
token
,
&
extra
);
add_token
(
&
t
,
coef_probs
[
band
[
c
]][
pt
],
#if CONFIG_ANS || CONFIG_DAALA_EC
#if CONFIG_
R
ANS || CONFIG_DAALA_EC
(
const
aom_cdf_prob
(
*
)[
ENTROPY_TOKENS
])
&
coef_cdfs
[
band
[
c
]][
pt
],
#endif
// CONFIG_ANS
#endif
extra
,
(
uint8_t
)
token
,
(
uint8_t
)
skip_eob
,
counts
[
band
[
c
]][
pt
]);
token_cache
[
scan
[
c
]]
=
av1_pt_energy_class
[
token
];
...
...
@@ -525,7 +525,7 @@ static void tokenize_b(int plane, int block, int blk_row, int blk_col,
}
if
(
c
<
seg_eob
)
{
add_token
(
&
t
,
coef_probs
[
band
[
c
]][
pt
],
#if CONFIG_ANS || CONFIG_DAALA_EC
#if CONFIG_
R
ANS || CONFIG_DAALA_EC
NULL
,
#endif
0
,
EOB_TOKEN
,
0
,
counts
[
band
[
c
]][
pt
]);
...
...
av1/encoder/tokenize.h
View file @
cfc5ac50
...
...
@@ -46,9 +46,9 @@ typedef struct {
extern
const
aom_tree_index
av1_coef_tree
[];
extern
const
aom_tree_index
av1_coef_con_tree
[];
#if !CONFIG_ANS
#if !CONFIG_
R
ANS
extern
const
struct
av1_token
av1_coef_encodings
[];
#endif // !CONFIG_ANS
#endif // !CONFIG_
R
ANS
int
av1_is_skippable_in_plane
(
MACROBLOCK
*
x
,
BLOCK_SIZE
bsize
,
int
plane
);
int
av1_has_high_freq_in_plane
(
MACROBLOCK
*
x
,
BLOCK_SIZE
bsize
,
int
plane
);
...
...
configure
View file @
cfc5ac50
...
...
@@ -271,6 +271,7 @@ EXPERIMENT_LIST="
new_quant
supertx
ans
rans
loop_restoration
ext_partition
ext_partition_types
...
...
Write
Preview
Supports
Markdown
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