misc(formatting): ran cargo fmt
and cargo clippy
,
fixed all warnings
This commit is contained in:
parent
cb4de8ae73
commit
fb3f79b7d4
4 changed files with 131 additions and 113 deletions
|
@ -28,7 +28,6 @@ impl SystemSerde {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct System {
|
pub struct System {
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
|
@ -42,13 +41,12 @@ pub struct System {
|
||||||
|
|
||||||
impl Ord for System {
|
impl Ord for System {
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
return self.id.cmp(&other.id);
|
self.id.cmp(&other.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for System {
|
impl PartialOrd for System {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
return Some(self.cmp(other));
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,38 +2,40 @@ extern crate strsim;
|
||||||
mod common;
|
mod common;
|
||||||
mod preprocess;
|
mod preprocess;
|
||||||
mod route;
|
mod route;
|
||||||
use std::collections::HashMap;
|
use common::{System, SystemSerde};
|
||||||
use pyo3::exceptions::*;
|
use pyo3::exceptions::*;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use pyo3::types::{PyDict, PyList};
|
use pyo3::types::{PyDict, PyList};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use common::{System,SystemSerde};
|
|
||||||
|
|
||||||
fn find_matches(path:&PathBuf,names:Vec<String>) -> Result<HashMap<String,(f64,Option<System>)>,String> {
|
fn find_matches(
|
||||||
let mut best: HashMap<String,(f64,Option<System>)> = HashMap::new();
|
path: &PathBuf,
|
||||||
|
names: Vec<String>,
|
||||||
|
) -> Result<HashMap<String, (f64, Option<System>)>, String> {
|
||||||
|
let mut best: HashMap<String, (f64, Option<System>)> = HashMap::new();
|
||||||
for name in &names {
|
for name in &names {
|
||||||
best.insert(name.to_string(),(0.0,None));
|
best.insert(name.to_string(), (0.0, None));
|
||||||
};
|
}
|
||||||
let mut reader = match csv::ReaderBuilder::new().from_path(path) {
|
let mut reader = match csv::ReaderBuilder::new().from_path(path) {
|
||||||
Ok(rdr) => rdr,
|
Ok(rdr) => rdr,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(format!("Error opening {}: {}", path.to_str().unwrap(), e).to_string());
|
return Err(format!("Error opening {}: {}", path.to_str().unwrap(), e).to_string());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let systems=reader
|
let systems = reader.deserialize::<SystemSerde>();
|
||||||
.deserialize::<SystemSerde>();
|
|
||||||
for sys in systems {
|
for sys in systems {
|
||||||
let sys=sys.unwrap();
|
let sys = sys.unwrap();
|
||||||
for name in &names {
|
for name in &names {
|
||||||
best.entry(name.clone()).and_modify(|ent| {
|
best.entry(name.clone()).and_modify(|ent| {
|
||||||
let d1=strsim::normalized_levenshtein(&sys.system,&name);
|
let d1 = strsim::normalized_levenshtein(&sys.system, &name);
|
||||||
if d1>ent.0 {
|
if d1 > ent.0 {
|
||||||
*ent=(d1,Some(sys.build()))
|
*ent = (d1, Some(sys.build()))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Ok(best);
|
Ok(best)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pymodule]
|
#[pymodule]
|
||||||
|
@ -67,7 +69,7 @@ pub fn _ed_lrr(_py: Python, m: &PyModule) -> PyResult<()> {
|
||||||
&PathBuf::from(infile_bodies),
|
&PathBuf::from(infile_bodies),
|
||||||
&PathBuf::from(infile_systems),
|
&PathBuf::from(infile_systems),
|
||||||
&PathBuf::from(outfile),
|
&PathBuf::from(outfile),
|
||||||
Box::new(callback_wrapped),
|
&callback_wrapped,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Ok(state.to_object(py))
|
Ok(state.to_object(py))
|
||||||
|
@ -78,14 +80,14 @@ pub fn _ed_lrr(_py: Python, m: &PyModule) -> PyResult<()> {
|
||||||
///
|
///
|
||||||
/// Find system by name
|
/// Find system by name
|
||||||
#[pyfn(m, "find_sys")]
|
#[pyfn(m, "find_sys")]
|
||||||
fn find_sys(py:Python,sys_names:Vec<String>,sys_list:String) -> PyResult<PyObject> {
|
fn find_sys(py: Python, sys_names: Vec<String>, sys_list: String) -> PyResult<PyObject> {
|
||||||
let path=PathBuf::from(sys_list);
|
let path = PathBuf::from(sys_list);
|
||||||
match find_matches(&path,sys_names) {
|
match find_matches(&path, sys_names) {
|
||||||
Ok(vals) => {
|
Ok(vals) => {
|
||||||
let ret=PyDict::new(py);
|
let ret = PyDict::new(py);
|
||||||
for (key,(diff,sys)) in vals {
|
for (key, (diff, sys)) in vals {
|
||||||
let ret_dict=PyDict::new(py);
|
let ret_dict = PyDict::new(py);
|
||||||
if let Some(val)= sys {
|
if let Some(val) = sys {
|
||||||
let pos = PyList::new(py, val.pos.iter());
|
let pos = PyList::new(py, val.pos.iter());
|
||||||
ret_dict.set_item("star_type", val.star_type.clone())?;
|
ret_dict.set_item("star_type", val.star_type.clone())?;
|
||||||
ret_dict.set_item("system", val.system.clone())?;
|
ret_dict.set_item("system", val.system.clone())?;
|
||||||
|
@ -93,13 +95,13 @@ pub fn _ed_lrr(_py: Python, m: &PyModule) -> PyResult<()> {
|
||||||
ret_dict.set_item("distance", val.distance)?;
|
ret_dict.set_item("distance", val.distance)?;
|
||||||
ret_dict.set_item("pos", pos)?;
|
ret_dict.set_item("pos", pos)?;
|
||||||
ret_dict.set_item("id", val.id)?;
|
ret_dict.set_item("id", val.id)?;
|
||||||
ret.set_item(key,(diff,ret_dict).to_object(py))?;
|
ret.set_item(key, (diff, ret_dict).to_object(py))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Ok(ret.to_object(py));
|
Ok(ret.to_object(py))
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(PyErr::new::<ValueError, _>(e));
|
Err(PyErr::new::<ValueError, _>(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,11 +149,11 @@ pub fn _ed_lrr(_py: Python, m: &PyModule) -> PyResult<()> {
|
||||||
state_dict.set_item("prc_seen", state.prc_seen)?;
|
state_dict.set_item("prc_seen", state.prc_seen)?;
|
||||||
callback.call(py, (state_dict,), None)
|
callback.call(py, (state_dict,), None)
|
||||||
};
|
};
|
||||||
let mut systems=Vec::new();
|
let mut systems = Vec::new();
|
||||||
for sys in hops {
|
for sys in hops {
|
||||||
systems.push(route::SysEntry::parse(&sys))
|
systems.push(route::SysEntry::parse(&sys))
|
||||||
}
|
}
|
||||||
println!("SYSTEMS: {:?}",systems);
|
println!("SYSTEMS: {:?}", systems);
|
||||||
let opts = RouteOpts {
|
let opts = RouteOpts {
|
||||||
systems,
|
systems,
|
||||||
range: Some(range),
|
range: Some(range),
|
||||||
|
@ -165,7 +167,7 @@ pub fn _ed_lrr(_py: Python, m: &PyModule) -> PyResult<()> {
|
||||||
keep_first,
|
keep_first,
|
||||||
keep_last,
|
keep_last,
|
||||||
primary,
|
primary,
|
||||||
radius_mult
|
radius_mult,
|
||||||
};
|
};
|
||||||
let none = ().to_object(py);
|
let none = ().to_object(py);
|
||||||
match route(opts) {
|
match route(opts) {
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
use crate::common::SystemSerde;
|
use crate::common::SystemSerde;
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
|
use pyo3::prelude::*;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::Result;
|
use serde_json::Result;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Seek;
|
use std::io::Seek;
|
||||||
use std::io::{BufRead, BufReader, BufWriter, SeekFrom};
|
use std::io::{BufRead, BufReader, BufWriter, SeekFrom};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::Instant;
|
|
||||||
use std::str;
|
use std::str;
|
||||||
use pyo3::prelude::*;
|
use std::time::Instant;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
@ -48,7 +48,6 @@ pub struct PreprocessState {
|
||||||
pub count: usize,
|
pub count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn get_mult(star_type: &str) -> f32 {
|
fn get_mult(star_type: &str) -> f32 {
|
||||||
if star_type.contains("White Dwarf") {
|
if star_type.contains("White Dwarf") {
|
||||||
return 1.5;
|
return 1.5;
|
||||||
|
@ -59,18 +58,22 @@ fn get_mult(star_type: &str) -> f32 {
|
||||||
1.0
|
1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process(path: &PathBuf, func: &mut dyn for<'r> FnMut(&'r str) -> (),callback: &Box<dyn Fn(&PreprocessState) -> PyResult<PyObject>>) -> std::io::Result<()> {
|
fn process(
|
||||||
|
path: &PathBuf,
|
||||||
|
func: &mut dyn for<'r> FnMut(&'r str) -> (),
|
||||||
|
callback: &dyn Fn(&PreprocessState) -> PyResult<PyObject>,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
let fh = File::open(path)?;
|
let fh = File::open(path)?;
|
||||||
let total_size= fh.metadata()?.len();
|
let total_size = fh.metadata()?.len();
|
||||||
let mut t_last=Instant::now();
|
let mut t_last = Instant::now();
|
||||||
let mut reader = BufReader::new(fh);
|
let mut reader = BufReader::new(fh);
|
||||||
let mut state=PreprocessState {
|
let mut state = PreprocessState {
|
||||||
file: path.to_str().unwrap().to_owned(),
|
file: path.to_str().unwrap().to_owned(),
|
||||||
total: total_size,
|
total: total_size,
|
||||||
done: 0,
|
done: 0,
|
||||||
count: 0 ,
|
count: 0,
|
||||||
message: format!("Processing {} ...", path.to_str().unwrap())
|
message: format!("Processing {} ...", path.to_str().unwrap()),
|
||||||
};
|
};
|
||||||
println!("Loading {} ...", path.to_str().unwrap());
|
println!("Loading {} ...", path.to_str().unwrap());
|
||||||
while let Ok(n) = reader.read_line(&mut buffer) {
|
while let Ok(n) = reader.read_line(&mut buffer) {
|
||||||
|
@ -81,28 +84,35 @@ fn process(path: &PathBuf, func: &mut dyn for<'r> FnMut(&'r str) -> (),callback:
|
||||||
if !buffer.is_empty() {
|
if !buffer.is_empty() {
|
||||||
func(&buffer);
|
func(&buffer);
|
||||||
}
|
}
|
||||||
let pos=reader.seek(SeekFrom::Current(0)).unwrap();
|
let pos = reader.seek(SeekFrom::Current(0)).unwrap();
|
||||||
state.done=pos;
|
state.done = pos;
|
||||||
state.count += 1;
|
state.count += 1;
|
||||||
if t_last.elapsed().as_millis()>100 {
|
if t_last.elapsed().as_millis() > 100 {
|
||||||
callback(&state)?;
|
callback(&state)?;
|
||||||
t_last=Instant::now();
|
t_last = Instant::now();
|
||||||
}
|
}
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_systems(path: &PathBuf,callback: &Box<dyn Fn(&PreprocessState) -> PyResult<PyObject>> ) -> FnvHashMap<i32, System> {
|
fn process_systems(
|
||||||
|
path: &PathBuf,
|
||||||
|
callback: &dyn Fn(&PreprocessState) -> PyResult<PyObject>,
|
||||||
|
) -> FnvHashMap<i32, System> {
|
||||||
let mut ret = FnvHashMap::default();
|
let mut ret = FnvHashMap::default();
|
||||||
process(path, &mut |line| {
|
process(
|
||||||
|
path,
|
||||||
|
&mut |line| {
|
||||||
let sys_res: Result<System> = serde_json::from_str(&line);
|
let sys_res: Result<System> = serde_json::from_str(&line);
|
||||||
if let Ok(sys) = sys_res {
|
if let Ok(sys) = sys_res {
|
||||||
ret.insert(sys.id, sys);
|
ret.insert(sys.id, sys);
|
||||||
} else {
|
} else {
|
||||||
eprintln!("\nError parsing: {}\n\t{:?}\n", line, sys_res.unwrap_err());
|
eprintln!("\nError parsing: {}\n\t{:?}\n", line, sys_res.unwrap_err());
|
||||||
}
|
}
|
||||||
},callback)
|
},
|
||||||
|
callback,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
@ -125,7 +135,7 @@ fn process_bodies(
|
||||||
path: &PathBuf,
|
path: &PathBuf,
|
||||||
out_path: &PathBuf,
|
out_path: &PathBuf,
|
||||||
systems: &mut FnvHashMap<i32, System>,
|
systems: &mut FnvHashMap<i32, System>,
|
||||||
callback: &Box<dyn Fn(&PreprocessState) -> PyResult<PyObject>>,
|
callback: &dyn Fn(&PreprocessState) -> PyResult<PyObject>,
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
println!(
|
println!(
|
||||||
"Processing {} into {} ...",
|
"Processing {} into {} ...",
|
||||||
|
@ -134,7 +144,9 @@ fn process_bodies(
|
||||||
);
|
);
|
||||||
let mut n: u32 = 0;
|
let mut n: u32 = 0;
|
||||||
let mut wtr = csv::Writer::from_path(out_path)?;
|
let mut wtr = csv::Writer::from_path(out_path)?;
|
||||||
process(path, &mut |line| {
|
process(
|
||||||
|
path,
|
||||||
|
&mut |line| {
|
||||||
if !line.contains("Star") {
|
if !line.contains("Star") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -164,17 +176,24 @@ fn process_bodies(
|
||||||
} else {
|
} else {
|
||||||
eprintln!("\nError parsing: {}\n\t{:?}\n", line, body_res.unwrap_err());
|
eprintln!("\nError parsing: {}\n\t{:?}\n", line, body_res.unwrap_err());
|
||||||
}
|
}
|
||||||
},callback)
|
},
|
||||||
|
callback,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
println!("Total Systems: {}", n);
|
println!("Total Systems: {}", n);
|
||||||
systems.clear();
|
systems.clear();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn preprocess_files(bodies: &PathBuf,systems:&PathBuf,out_path:&PathBuf,callback: Box<dyn Fn(&PreprocessState) -> PyResult<PyObject>>) -> std::io::Result<()> {
|
pub fn preprocess_files(
|
||||||
|
bodies: &PathBuf,
|
||||||
|
systems: &PathBuf,
|
||||||
|
out_path: &PathBuf,
|
||||||
|
callback: &dyn Fn(&PreprocessState) -> PyResult<PyObject>,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
if !out_path.exists() {
|
if !out_path.exists() {
|
||||||
let mut systems = process_systems(systems,&callback);
|
let mut systems = process_systems(systems, &callback);
|
||||||
process_bodies(bodies, out_path, &mut systems,&callback)?;
|
process_bodies(bodies, out_path, &mut systems, &callback)?;
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
"File '{}' exists, not overwriting it",
|
"File '{}' exists, not overwriting it",
|
||||||
|
|
|
@ -104,6 +104,7 @@ impl System {
|
||||||
pub fn dist2(&self, p: &[f32; 3]) -> f32 {
|
pub fn dist2(&self, p: &[f32; 3]) -> f32 {
|
||||||
dist2(&self.pos, p)
|
dist2(&self.pos, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn distp(&self, p: &System) -> f32 {
|
pub fn distp(&self, p: &System) -> f32 {
|
||||||
dist(&self.pos, &p.pos)
|
dist(&self.pos, &p.pos)
|
||||||
}
|
}
|
||||||
|
@ -320,12 +321,12 @@ impl Router {
|
||||||
return scoopable;
|
return scoopable;
|
||||||
}
|
}
|
||||||
let df = src.distp(dst);
|
let df = src.distp(dst);
|
||||||
return (sys.distp(src) + sys.distp(dst)) < (df * (1.0 + self.radius_mult));
|
(sys.distp(src) + sys.distp(dst)) < (df * (1.0 + self.radius_mult))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn best_multiroute(
|
pub fn best_multiroute(
|
||||||
&self,
|
&self,
|
||||||
waypoints: &Vec<System>,
|
waypoints: &[System],
|
||||||
range: f32,
|
range: f32,
|
||||||
keep: (bool, bool),
|
keep: (bool, bool),
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
@ -367,14 +368,14 @@ impl Router {
|
||||||
|
|
||||||
pub fn multiroute(
|
pub fn multiroute(
|
||||||
&self,
|
&self,
|
||||||
waypoints: &Vec<System>,
|
waypoints: &[System],
|
||||||
range: f32,
|
range: f32,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
factor: f32,
|
factor: f32,
|
||||||
) -> Result<Vec<System>, String> {
|
) -> Result<Vec<System>, String> {
|
||||||
let mut route: Vec<System> = Vec::new();
|
let mut route: Vec<System> = Vec::new();
|
||||||
for pair in waypoints.windows(2) {
|
for pair in waypoints.windows(2) {
|
||||||
match pair.clone() {
|
match pair {
|
||||||
[src, dst] => {
|
[src, dst] => {
|
||||||
let block = match mode {
|
let block = match mode {
|
||||||
Mode::BFS => self.route_bfs(&src, &dst, range)?,
|
Mode::BFS => self.route_bfs(&src, &dst, range)?,
|
||||||
|
@ -423,13 +424,13 @@ impl Router {
|
||||||
for ent in systems {
|
for ent in systems {
|
||||||
match ent {
|
match ent {
|
||||||
SysEntry::ID(i) => match sys_by_id.get(i) {
|
SysEntry::ID(i) => match sys_by_id.get(i) {
|
||||||
Some(sys) => ret.push(sys.clone().clone()),
|
Some(sys) => ret.push((*sys).clone()),
|
||||||
None => {
|
None => {
|
||||||
return Err(format!("System: {:?} not found", ent));
|
return Err(format!("System: {:?} not found", ent));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
SysEntry::Name(n) => match sys_by_name.get(n) {
|
SysEntry::Name(n) => match sys_by_name.get(n) {
|
||||||
Some(sys) => ret.push(sys.clone().clone()),
|
Some(sys) => ret.push((*sys).clone()),
|
||||||
None => {
|
None => {
|
||||||
return Err(format!("System: {:?} not found", ent));
|
return Err(format!("System: {:?} not found", ent));
|
||||||
}
|
}
|
||||||
|
@ -509,9 +510,9 @@ impl Router {
|
||||||
.filter(|&nb| seen.insert(nb.id))
|
.filter(|&nb| seen.insert(nb.id))
|
||||||
.map(|nb| {
|
.map(|nb| {
|
||||||
prev.insert(nb.id, sys);
|
prev.insert(nb.id, sys);
|
||||||
let d_g=nb.distp(goal_sys);
|
let d_g = nb.distp(goal_sys);
|
||||||
if d_g<d_rem {
|
if d_g < d_rem {
|
||||||
d_rem=d_g;
|
d_rem = d_g;
|
||||||
}
|
}
|
||||||
(depth + 1, (d_g / range) as usize, nb)
|
(depth + 1, (d_g / range) as usize, nb)
|
||||||
}),
|
}),
|
||||||
|
@ -608,9 +609,9 @@ impl Router {
|
||||||
.filter(|&nb| seen.insert(nb.id))
|
.filter(|&nb| seen.insert(nb.id))
|
||||||
.map(|nb| {
|
.map(|nb| {
|
||||||
prev.insert(nb.id, sys);
|
prev.insert(nb.id, sys);
|
||||||
let d_g=nb.distp(goal_sys);
|
let d_g = nb.distp(goal_sys);
|
||||||
if d_g<d_rem {
|
if d_g < d_rem {
|
||||||
d_rem=d_g;
|
d_rem = d_g;
|
||||||
}
|
}
|
||||||
(-nb.mult, d_g, depth + 1, nb)
|
(-nb.mult, d_g, depth + 1, nb)
|
||||||
}),
|
}),
|
||||||
|
@ -912,9 +913,7 @@ pub fn route(opts: RouteOpts) -> Result<Option<Vec<System>>, String> {
|
||||||
};
|
};
|
||||||
let systems: Vec<System> = router
|
let systems: Vec<System> = router
|
||||||
.resolve_systems(&opts.systems)?
|
.resolve_systems(&opts.systems)?
|
||||||
.iter()
|
.to_vec();
|
||||||
.map(|sys| sys.clone())
|
|
||||||
.collect();
|
|
||||||
if opts.precompute {
|
if opts.precompute {
|
||||||
for sys in systems {
|
for sys in systems {
|
||||||
router.route_tree = None;
|
router.route_tree = None;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue