Added featurest to com_let to allow default low and high indices and to allow the last dimension to default to its full range
This commit is contained in:
parent
89b691e70f
commit
097e534ad2
|
|
@ -23,9 +23,11 @@ static void copy_vector_data(struct dvec *vec_dst,
|
||||||
static void copy_vector_data_with_stride(struct dvec *vec_dst,
|
static void copy_vector_data_with_stride(struct dvec *vec_dst,
|
||||||
const struct dvec *vec_src,
|
const struct dvec *vec_src,
|
||||||
int n_dst_index, const index_range_t *p_dst_index);
|
int n_dst_index, const index_range_t *p_dst_index);
|
||||||
static int find_indices(char *s, index_range_t *p_index, int *p_n_index);
|
static int find_indices(char *s, const struct dvec *vec_dst,
|
||||||
static int get_index_values(char *s, index_range_t *p_range);
|
index_range_t *p_index);
|
||||||
int get_one_index_value(char *s, int *p_index);
|
static int get_index_values(char *s, int n_elem_this_dim,
|
||||||
|
index_range_t *p_range);
|
||||||
|
int get_one_index_value(const char *s, int *p_index);
|
||||||
|
|
||||||
/* let <vec_name> = <expr>
|
/* let <vec_name> = <expr>
|
||||||
* let <vec_name>[<bracket_expr>] = <expr>
|
* let <vec_name>[<bracket_expr>] = <expr>
|
||||||
|
|
@ -44,6 +46,9 @@ void com_let(wordlist *wl)
|
||||||
struct dvec *vec_src = (struct dvec *) NULL;
|
struct dvec *vec_src = (struct dvec *) NULL;
|
||||||
char *rhs;
|
char *rhs;
|
||||||
|
|
||||||
|
/* Start of index NULL is a flag for no index */
|
||||||
|
char *p_index_start = (char *) NULL;
|
||||||
|
|
||||||
|
|
||||||
/* let with no arguments is equivalent to display */
|
/* let with no arguments is equivalent to display */
|
||||||
if (!wl) {
|
if (!wl) {
|
||||||
|
|
@ -66,15 +71,15 @@ void com_let(wordlist *wl)
|
||||||
* p = leftmost part of orig p up to first '['. So p always
|
* p = leftmost part of orig p up to first '['. So p always
|
||||||
* becomes the vector name, possibly with some spaces at the end. */
|
* becomes the vector name, possibly with some spaces at the end. */
|
||||||
if ((s = strchr(p, '[')) != NULL) {
|
if ((s = strchr(p, '[')) != NULL) {
|
||||||
|
/* This null makes the dest vector name a null-terminated string */
|
||||||
*s = '\0';
|
*s = '\0';
|
||||||
if (find_indices(s + 1, p_dst_index, &n_dst_index) != 0) {
|
p_index_start = s + 1;
|
||||||
txfree(p);
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
} /* end of case that an indexing bracket '[' was found */
|
|
||||||
|
|
||||||
|
/* "Remove" any spaces at the end of the vector name at p by stepping
|
||||||
/* "Remove" any spaces at the end of the vector name at p */
|
* from the end of the word to the first charatcter that is not
|
||||||
|
* whitespace and then overwriting the next character (which will be
|
||||||
|
* the original NULL if there was no whitespace) with a NULL. */
|
||||||
{
|
{
|
||||||
char *q;
|
char *q;
|
||||||
for (q = p + strlen(p) - 1; *q <= ' ' && p <= q; q--) {
|
for (q = p + strlen(p) - 1; *q <= ' ' && p <= q; q--) {
|
||||||
|
|
@ -89,14 +94,45 @@ void com_let(wordlist *wl)
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Locate the vector being assigned values. If NULL, the vector
|
||||||
|
* does not exist */
|
||||||
|
struct dvec *vec_dst = vec_get(p);
|
||||||
|
if (vec_dst != (struct dvec *) NULL) {
|
||||||
|
/* Fix-up dimension count and limit. Sometimes these are
|
||||||
|
* not set properly. If not set, give the vector 1 dimension and
|
||||||
|
* ensure the right length */
|
||||||
|
if (vec_dst->v_numdims < 1) {
|
||||||
|
vec_dst->v_numdims = 1;
|
||||||
|
}
|
||||||
|
if (vec_dst->v_numdims == 1) {
|
||||||
|
vec_dst->v_dims[0] = vec_dst->v_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the vector was indexed, find the indices */
|
||||||
|
if (p_index_start != (char *) NULL) {
|
||||||
|
/* Test for an attempt to index an undefined vector */
|
||||||
|
if (vec_dst == (struct dvec *) NULL) {
|
||||||
|
fprintf(cp_err,
|
||||||
|
"When creating a new vector, it cannot be indexed.\n");
|
||||||
|
goto quit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (find_indices(p_index_start, vec_dst, p_dst_index) != 0) {
|
||||||
|
txfree(p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
n_dst_index = vec_dst->v_numdims;
|
||||||
|
} /* end of case that an indexing bracket '[' was found */
|
||||||
|
|
||||||
|
|
||||||
/* Evaluate rhs */
|
/* Evaluate rhs */
|
||||||
names = ft_getpnames_from_string(rhs, TRUE);
|
if ((names = ft_getpnames_from_string(
|
||||||
if (names == (struct pnode *) NULL) {
|
rhs, TRUE)) == (struct pnode *) NULL) {
|
||||||
fprintf(cp_err, "Error: RHS \"%s\" invalid\n", rhs);
|
fprintf(cp_err, "Error: RHS \"%s\" invalid\n", rhs);
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
vec_src = ft_evaluate(names);
|
if ((vec_src = ft_evaluate(names)) == (struct dvec *) NULL) {
|
||||||
if (!vec_src) {
|
|
||||||
fprintf(cp_err, "Error: Can't evaluate \"%s\"\n", rhs);
|
fprintf(cp_err, "Error: Can't evaluate \"%s\"\n", rhs);
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
|
|
@ -106,7 +142,7 @@ void com_let(wordlist *wl)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix-up dimension count and limit. Sometimes these are
|
/* Fix-up dimension count and limit. Sometimes these are
|
||||||
* not set properly. If not set, make 1-d vector and ensure
|
* not set properly. If not set, give the vector 1 dimension and ensure
|
||||||
* the right length */
|
* the right length */
|
||||||
if (vec_src->v_numdims < 1) {
|
if (vec_src->v_numdims < 1) {
|
||||||
vec_src->v_numdims = 1;
|
vec_src->v_numdims = 1;
|
||||||
|
|
@ -115,20 +151,9 @@ void com_let(wordlist *wl)
|
||||||
vec_src->v_dims[0] = vec_src->v_length;
|
vec_src->v_dims[0] = vec_src->v_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Locate the vector being assigned values. If NULL, the vector
|
|
||||||
* does not exist */
|
|
||||||
struct dvec * vec_dst = vec_get(p);
|
|
||||||
|
|
||||||
if (vec_dst == (struct dvec *) NULL) {
|
if (vec_dst == (struct dvec *) NULL) {
|
||||||
/* p is not an existing vector. So make a new one equal to vec_src
|
/* p is not an existing vector. So make a new one equal to vec_src
|
||||||
* in all ways, except enforce that it is a permanent vector. */
|
* in all ways, except enforce that it is a permanent vector. */
|
||||||
if (n_dst_index > 0) {
|
|
||||||
fprintf(cp_err,
|
|
||||||
"When creating a new vector, it cannot be indexed.\n");
|
|
||||||
goto quit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create and assign a new vector */
|
|
||||||
vec_dst = dvec_alloc(copy(p),
|
vec_dst = dvec_alloc(copy(p),
|
||||||
vec_src->v_type,
|
vec_src->v_type,
|
||||||
vec_src->v_flags | VF_PERMANENT,
|
vec_src->v_flags | VF_PERMANENT,
|
||||||
|
|
@ -140,15 +165,6 @@ void com_let(wordlist *wl)
|
||||||
} /* end of case of new vector */
|
} /* end of case of new vector */
|
||||||
else {
|
else {
|
||||||
/* Existing vector.*/
|
/* Existing vector.*/
|
||||||
/* Fix-up dimension count and limit. Sometimes these are
|
|
||||||
* not set properly. If not set, make 1-d vector and ensure
|
|
||||||
* the right length */
|
|
||||||
if (vec_dst->v_numdims < 1) {
|
|
||||||
vec_dst->v_numdims = 1;
|
|
||||||
}
|
|
||||||
if (vec_dst->v_numdims == 1) {
|
|
||||||
vec_dst->v_dims[0] = vec_dst->v_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n_dst_index == 0) {
|
if (n_dst_index == 0) {
|
||||||
/* Not indexed, so make equal to source vector as if it
|
/* Not indexed, so make equal to source vector as if it
|
||||||
|
|
@ -219,29 +235,6 @@ void com_let(wordlist *wl)
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check dimension numbers */
|
|
||||||
if (n_dst_index != vec_dst->v_numdims) {
|
|
||||||
fprintf(cp_err, "Number of vector indices given (%d) "
|
|
||||||
"does not match the dimension of the vector (%d).\n",
|
|
||||||
n_dst_index, vec_dst->v_numdims);
|
|
||||||
goto quit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check dimension ranges */
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int *vec_dst_dims = vec_dst->v_dims;
|
|
||||||
for (i = 0; i < n_dst_index; ++i) {
|
|
||||||
const int n_dst_cur = vec_dst_dims[i];
|
|
||||||
if (p_dst_index[i].high >= n_dst_cur) {
|
|
||||||
fprintf(cp_err,
|
|
||||||
"Vector index %d out of range (%d).\n",
|
|
||||||
i + 1, n_dst_cur);
|
|
||||||
goto quit;
|
|
||||||
}
|
|
||||||
} /* end of loop over dimensions */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* OK to copy, so copy */
|
/* OK to copy, so copy */
|
||||||
copy_vector_data_with_stride(vec_dst, vec_src,
|
copy_vector_data_with_stride(vec_dst, vec_src,
|
||||||
n_dst_index, p_dst_index);
|
n_dst_index, p_dst_index);
|
||||||
|
|
@ -269,20 +262,25 @@ quit:
|
||||||
|
|
||||||
/* Process indexing portion of a let command. On entry, s is the address
|
/* Process indexing portion of a let command. On entry, s is the address
|
||||||
* of the first byte after the first opening index bracket */
|
* of the first byte after the first opening index bracket */
|
||||||
static int find_indices(char *s, index_range_t *p_index, int *p_n_index)
|
static int find_indices(char *s, const struct dvec *vec_dst,
|
||||||
|
index_range_t *p_index)
|
||||||
{
|
{
|
||||||
|
const int v_numdims_dst = vec_dst->v_numdims;
|
||||||
|
const int * const v_dims_dst = vec_dst->v_dims;
|
||||||
|
int dim_cur = 0; /* current dimension being set */
|
||||||
|
|
||||||
|
|
||||||
/* Can be either comma-separated or individual dimensions */
|
/* Can be either comma-separated or individual dimensions */
|
||||||
if (strchr(s, ',') != 0) { /* has commas */
|
if (strchr(s, ',') != 0) { /* has commas */
|
||||||
char *p_end;
|
char *p_end;
|
||||||
int dim_cur = 0;
|
|
||||||
const int dim_max = MAXDIMS - 1;
|
|
||||||
while ((p_end = strchr(s, ',')) != (char *) NULL) {
|
while ((p_end = strchr(s, ',')) != (char *) NULL) {
|
||||||
*p_end = '\0';
|
*p_end = '\0';
|
||||||
if (dim_cur == dim_max) {
|
if (dim_cur == v_numdims_dst) {
|
||||||
(void) fprintf(cp_err, "Too many dimensions given.\n");
|
(void) fprintf(cp_err, "Too many dimensions given.\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (get_index_values(s, p_index + dim_cur) != 0) {
|
if (get_index_values(s, v_dims_dst[dim_cur],
|
||||||
|
p_index + dim_cur) != 0) {
|
||||||
(void) fprintf(cp_err, "Dimension ranges "
|
(void) fprintf(cp_err, "Dimension ranges "
|
||||||
"for dimension %d could not be found.\n",
|
"for dimension %d could not be found.\n",
|
||||||
dim_cur + 1);
|
dim_cur + 1);
|
||||||
|
|
@ -300,12 +298,13 @@ static int find_indices(char *s, index_range_t *p_index, int *p_n_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
*p_end = '\0';
|
*p_end = '\0';
|
||||||
if (dim_cur == dim_max) {
|
if (dim_cur == v_numdims_dst) {
|
||||||
(void) fprintf(cp_err,
|
(void) fprintf(cp_err,
|
||||||
"Final dimension exceded maximum number.\n");
|
"Final dimension exceded maximum number.\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (get_index_values(s, p_index + dim_cur) != 0) {
|
if (get_index_values(s, v_dims_dst[dim_cur],
|
||||||
|
p_index + dim_cur) != 0) {
|
||||||
(void) fprintf(cp_err, "Dimension ranges "
|
(void) fprintf(cp_err, "Dimension ranges "
|
||||||
"for last dimension (%d) could not be found.\n",
|
"for last dimension (%d) could not be found.\n",
|
||||||
dim_cur + 1);
|
dim_cur + 1);
|
||||||
|
|
@ -321,21 +320,18 @@ static int find_indices(char *s, index_range_t *p_index, int *p_n_index)
|
||||||
s);
|
s);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
*p_n_index = dim_cur;
|
|
||||||
return 0;
|
|
||||||
} /* end of case x[ , , ] */
|
} /* end of case x[ , , ] */
|
||||||
else { /* x[][][] */
|
else { /* x[][][] */
|
||||||
char *p_end;
|
char *p_end;
|
||||||
int dim_cur = 0;
|
|
||||||
const int dim_max = MAXDIMS - 1;
|
|
||||||
while ((p_end = strchr(s, ']')) != (char *) NULL) {
|
while ((p_end = strchr(s, ']')) != (char *) NULL) {
|
||||||
*p_end = '\0';
|
*p_end = '\0';
|
||||||
if (dim_cur == dim_max) {
|
if (dim_cur == v_numdims_dst) {
|
||||||
(void) fprintf(cp_err, "Too many dimensions given.\n");
|
(void) fprintf(cp_err, "Too many dimensions given. "
|
||||||
|
"Only %d are present.\n", v_numdims_dst);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (get_index_values(s, p_index + dim_cur) != 0) {
|
if (get_index_values(s, v_dims_dst[dim_cur],
|
||||||
|
p_index + dim_cur) != 0) {
|
||||||
(void) fprintf(cp_err, "Dimension ranges "
|
(void) fprintf(cp_err, "Dimension ranges "
|
||||||
"for dimension %d could not be found.\n",
|
"for dimension %d could not be found.\n",
|
||||||
dim_cur + 1);
|
dim_cur + 1);
|
||||||
|
|
@ -344,8 +340,7 @@ static int find_indices(char *s, index_range_t *p_index, int *p_n_index)
|
||||||
++dim_cur;
|
++dim_cur;
|
||||||
s = p_end + 1; /* after (former) ']' */
|
s = p_end + 1; /* after (former) ']' */
|
||||||
if (*(s = skip_ws(s)) == '\0') { /* reached end */
|
if (*(s = skip_ws(s)) == '\0') { /* reached end */
|
||||||
*p_n_index = dim_cur;
|
break;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Not end of expression, so must be '[' */
|
/* Not end of expression, so must be '[' */
|
||||||
|
|
@ -358,11 +353,35 @@ static int find_indices(char *s, index_range_t *p_index, int *p_n_index)
|
||||||
s++; /* past '[' */
|
s++; /* past '[' */
|
||||||
} /* end of loop over individual bracketed entries */
|
} /* end of loop over individual bracketed entries */
|
||||||
|
|
||||||
/* Did not find a single ']' in the string */
|
if (dim_cur == 0) {
|
||||||
(void) fprintf(cp_err, "The ']' for dimension 1 "
|
/* Did not find a single ']' in the string */
|
||||||
"could not be found.\n");
|
(void) fprintf(cp_err, "The ']' for dimension 1 "
|
||||||
return -1;
|
"could not be found.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
} /* end of case x[][][][] */
|
} /* end of case x[][][][] */
|
||||||
|
|
||||||
|
/* Finalize dimensions. There must be the same number or one less than
|
||||||
|
* the number of dimensions of the vector. For the special case of one
|
||||||
|
* less, the final dimension is set to the full range of the last
|
||||||
|
* dimension. Note that checks for too many dimensions have already
|
||||||
|
* been performed while the index information was found. */
|
||||||
|
if (dim_cur != v_numdims_dst) { /* special case or error */
|
||||||
|
if (dim_cur == v_numdims_dst - 1) { /* special case */
|
||||||
|
index_range_t * const p_index_last = p_index + dim_cur;
|
||||||
|
p_index_last->low = 0;
|
||||||
|
p_index_last->high = vec_dst->v_dims[dim_cur] - 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(void) fprintf(cp_err, "Error: Only %d dimensions "
|
||||||
|
"were supplied, but %d are needed. The last dimension "
|
||||||
|
"may be omitted, in which case it will defalt to the "
|
||||||
|
"full range of that dimension.\n",
|
||||||
|
dim_cur, v_numdims_dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
} /* end of function find_indices */
|
} /* end of function find_indices */
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -370,9 +389,13 @@ static int find_indices(char *s, index_range_t *p_index, int *p_n_index)
|
||||||
/* Convert expresion expr -> low and high ranges equal or
|
/* Convert expresion expr -> low and high ranges equal or
|
||||||
* expression expr1 : epr2 -> low = expr1 and high = expr2.
|
* expression expr1 : epr2 -> low = expr1 and high = expr2.
|
||||||
* Values are tested to ensure they are positive and that the low
|
* Values are tested to ensure they are positive and that the low
|
||||||
* value does not exceed the high value. Since the extent of the index
|
* value does not exceed the high value.
|
||||||
* is not known, that cannot be checked. */
|
*
|
||||||
static int get_index_values(char *s, index_range_t *p_range)
|
* If expr1 is whitespace or empty, it defaults to 0. For high, the
|
||||||
|
* largest possible values is used.
|
||||||
|
*/
|
||||||
|
static int get_index_values(char *s, int n_elem_this_dim,
|
||||||
|
index_range_t *p_range)
|
||||||
{
|
{
|
||||||
char *p_colon;
|
char *p_colon;
|
||||||
if ((p_colon = strchr(s, ':')) == (char *) NULL) { /* One expression */
|
if ((p_colon = strchr(s, ':')) == (char *) NULL) { /* One expression */
|
||||||
|
|
@ -382,32 +405,65 @@ static int get_index_values(char *s, index_range_t *p_range)
|
||||||
}
|
}
|
||||||
p_range->high = p_range->low;
|
p_range->high = p_range->low;
|
||||||
}
|
}
|
||||||
else { /* l:h */
|
else { /* l:h. If l defaults to 0 and h to dim size - 1 */
|
||||||
*p_colon = '\0';
|
*p_colon = '\0';
|
||||||
if (get_one_index_value(s, &p_range->low) != 0) {
|
{
|
||||||
(void) fprintf(cp_err, "Error geting low range.\n");
|
const int rc = get_one_index_value(s, &p_range->low);
|
||||||
return -1;
|
if (rc != 0) {
|
||||||
|
if (rc < 0) { /* error */
|
||||||
|
(void) fprintf(cp_err, "Error geting low range.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* +1 -> Else use defalt */
|
||||||
|
p_range->low = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
s = p_colon + 1; /* past (former) colon */
|
s = p_colon + 1; /* past (former) colon */
|
||||||
if (get_one_index_value(s, &p_range->high) != 0) {
|
{
|
||||||
(void) fprintf(cp_err, "Error geting high range.\n");
|
const int rc = get_one_index_value(s, &p_range->high);
|
||||||
return -1;
|
if (rc != 0) {
|
||||||
|
if (rc < 0) { /* error */
|
||||||
|
(void) fprintf(cp_err, "Error geting high range.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* +1 -> Else use defalt */
|
||||||
|
p_range->high = n_elem_this_dim - 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Ensure ranges given were valid */
|
||||||
if (p_range->low > p_range->high) {
|
if (p_range->low > p_range->high) {
|
||||||
(void) fprintf(cp_err, "Error low range (%d) is greater "
|
(void) fprintf(cp_err, "Error: low range (%d) is greater "
|
||||||
"than high range (%d).\n",
|
"than high range (%d).\n",
|
||||||
p_range->low, p_range->high);
|
p_range->low, p_range->high);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (p_range->high >= n_elem_this_dim) {
|
||||||
|
(void) fprintf(cp_err, "Error: high range (%d) exceeds "
|
||||||
|
"the maximum value (%d).\n",
|
||||||
|
p_range->high, n_elem_this_dim - 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
} /* end of function get_index_values */
|
} /* end of function get_index_values */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Get an index value */
|
/* Get an index value
|
||||||
int get_one_index_value(char *s, int *p_index)
|
*
|
||||||
|
* Return codes
|
||||||
|
* +1: String empty or all whitespace
|
||||||
|
* 0: Normal
|
||||||
|
* -1: Error
|
||||||
|
*/
|
||||||
|
int get_one_index_value(const char *s, int *p_index)
|
||||||
{
|
{
|
||||||
|
/* Test for a string of whitespace */
|
||||||
|
if (*(s = skip_ws(s)) == '\0') {
|
||||||
|
return +1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Parse the expression */
|
/* Parse the expression */
|
||||||
struct pnode * const names = ft_getpnames_from_string(s, TRUE);
|
struct pnode * const names = ft_getpnames_from_string(s, TRUE);
|
||||||
if (names == (struct pnode *) NULL) {
|
if (names == (struct pnode *) NULL) {
|
||||||
|
|
@ -447,7 +503,7 @@ int get_one_index_value(char *s, int *p_index)
|
||||||
free_pnode_x(names);
|
free_pnode_x(names);
|
||||||
|
|
||||||
return xrc;
|
return xrc;
|
||||||
} /* end of function get_one_index_value */
|
} /* end of function get_one_index_value */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue