Use Dijkstras algorithm
This commit is contained in:
parent
5ff1afc26b
commit
9facddf889
40
Cargo.lock
generated
40
Cargo.lock
generated
@ -8,6 +8,7 @@ dependencies = [
|
|||||||
"geo 0.12.0 (git+https://github.com/FliegendeWurst/geo)",
|
"geo 0.12.0 (git+https://github.com/FliegendeWurst/geo)",
|
||||||
"hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"pathfinding 1.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rstar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rstar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"svg 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"svg 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -81,6 +82,11 @@ dependencies = [
|
|||||||
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "failure"
|
name = "failure"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@ -101,6 +107,11 @@ dependencies = [
|
|||||||
"synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fixedbitset"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "float-cmp"
|
name = "float-cmp"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@ -146,6 +157,19 @@ dependencies = [
|
|||||||
"unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@ -185,6 +209,17 @@ dependencies = [
|
|||||||
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pathfinding"
|
||||||
|
version = "1.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pdqselect"
|
name = "pdqselect"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -412,19 +447,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4"
|
"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4"
|
||||||
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
|
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
|
||||||
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
|
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
|
||||||
|
"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b"
|
||||||
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
|
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
|
||||||
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
|
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
|
||||||
|
"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33"
|
||||||
"checksum float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "134a8fa843d80a51a5b77d36d42bc2def9edcb0262c914861d08129fd1926600"
|
"checksum float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "134a8fa843d80a51a5b77d36d42bc2def9edcb0262c914861d08129fd1926600"
|
||||||
"checksum geo 0.12.0 (git+https://github.com/FliegendeWurst/geo)" = "<none>"
|
"checksum geo 0.12.0 (git+https://github.com/FliegendeWurst/geo)" = "<none>"
|
||||||
"checksum geo-types 0.4.1 (git+https://github.com/FliegendeWurst/geo)" = "<none>"
|
"checksum geo-types 0.4.1 (git+https://github.com/FliegendeWurst/geo)" = "<none>"
|
||||||
"checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da"
|
"checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da"
|
||||||
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
|
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
|
||||||
|
"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d"
|
||||||
|
"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
|
||||||
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
|
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
|
||||||
"checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917"
|
"checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917"
|
||||||
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
|
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
|
||||||
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
|
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
|
||||||
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
|
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
|
||||||
"checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba"
|
"checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba"
|
||||||
|
"checksum pathfinding 1.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "37691aaf6640549d85ed79575cb159843b07380d420aac9e891b627e7cc3f1f3"
|
||||||
"checksum pdqselect 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27"
|
"checksum pdqselect 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27"
|
||||||
"checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18"
|
"checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18"
|
||||||
"checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0"
|
"checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0"
|
||||||
|
@ -19,3 +19,4 @@ svgdom = "0.16.1"
|
|||||||
rstar = "0.2.0"
|
rstar = "0.2.0"
|
||||||
lazy_static = "1.3.0"
|
lazy_static = "1.3.0"
|
||||||
hashbrown = "0.1.8"
|
hashbrown = "0.1.8"
|
||||||
|
pathfinding = "1.1.12"
|
||||||
|
103
src/main.rs
103
src/main.rs
@ -2,15 +2,15 @@ use chrono::{Duration, NaiveTime};
|
|||||||
use geo::prelude::*;
|
use geo::prelude::*;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use pathfinding::directed::dijkstra::dijkstra;
|
||||||
use rstar::{RTree, RTreeObject};
|
use rstar::{RTree, RTreeObject};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::collections::BinaryHeap;
|
|
||||||
use std::f32;
|
use std::f32;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use std::time::{self, Instant};
|
|
||||||
|
|
||||||
mod display;
|
mod display;
|
||||||
mod input;
|
mod input;
|
||||||
@ -26,10 +26,10 @@ type Polygon = geo::Polygon<f32>;
|
|||||||
struct Opt {
|
struct Opt {
|
||||||
/// Debug-Modus aktivieren
|
/// Debug-Modus aktivieren
|
||||||
#[structopt(short = "d", long = "debug")]
|
#[structopt(short = "d", long = "debug")]
|
||||||
debug: bool,
|
_debug: bool,
|
||||||
/// Zwischenlösungen speichern
|
/// Zwischenlösungen speichern
|
||||||
#[structopt(short = "s", long = "save")]
|
#[structopt(short = "s", long = "save")]
|
||||||
save_intermediates: bool,
|
_save_intermediates: bool,
|
||||||
/// Beste Lösung als tkz-Code ausgeben
|
/// Beste Lösung als tkz-Code ausgeben
|
||||||
#[structopt(short = "t", long = "tkz")]
|
#[structopt(short = "t", long = "tkz")]
|
||||||
tkz: bool,
|
tkz: bool,
|
||||||
@ -42,7 +42,7 @@ struct Opt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Möglicher Zustand von Lisas Rennen
|
/// Möglicher Zustand von Lisas Rennen
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
struct RunState {
|
struct RunState {
|
||||||
/// Theoretisch mögliche Startposition des Busses
|
/// Theoretisch mögliche Startposition des Busses
|
||||||
bus: f32,
|
bus: f32,
|
||||||
@ -52,15 +52,16 @@ struct RunState {
|
|||||||
total_distance: f32,
|
total_distance: f32,
|
||||||
}
|
}
|
||||||
/// Trait-Implementierung, damit die Struktur in BinaryHeaps passt
|
/// Trait-Implementierung, damit die Struktur in BinaryHeaps passt
|
||||||
|
impl PartialEq for RunState {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.pos.x() == other.pos.x() && self.pos.y() == other.pos.y()
|
||||||
|
}
|
||||||
|
}
|
||||||
impl cmp::Eq for RunState {}
|
impl cmp::Eq for RunState {}
|
||||||
impl cmp::Ord for RunState {
|
impl Hash for RunState {
|
||||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.bus.partial_cmp(&other.bus).unwrap()
|
(self.pos.x() as u32).hash(state);
|
||||||
}
|
(self.pos.y() as u32).hash(state);
|
||||||
}
|
|
||||||
impl cmp::PartialOrd for RunState {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,27 +131,10 @@ fn main() {
|
|||||||
calc_time(distance(meeting_point, house), meeting_point.y())
|
calc_time(distance(meeting_point, house), meeting_point.y())
|
||||||
);
|
);
|
||||||
|
|
||||||
// Zustände in Max-Heap sortieren
|
let mut best = dijkstra(
|
||||||
let mut states = BinaryHeap::new();
|
&vec![start],
|
||||||
states.push(vec![start]);
|
|state| {
|
||||||
|
|
||||||
// beste Lösung speichern
|
|
||||||
let mut best_bus = f32::NEG_INFINITY;
|
|
||||||
let mut best = vec![];
|
|
||||||
// Zwischenlösungen speichern
|
|
||||||
let save_prefix = "route_";
|
|
||||||
let mut save_counter = 0;
|
|
||||||
// Debug-Statistiken
|
|
||||||
let mut total_time = time::Duration::from_secs(0);
|
|
||||||
let mut iterations = 0;
|
|
||||||
|
|
||||||
// weitersuchen, bis kein besserer Zustand mehr vorhanden ist
|
|
||||||
while states.peek().map(|x| x[0].bus > best_bus) == Some(true) {
|
|
||||||
let start = Instant::now();
|
|
||||||
// besten Zustand einlesen
|
|
||||||
let state = states.pop().unwrap();
|
|
||||||
let last = &state[0];
|
let last = &state[0];
|
||||||
|
|
||||||
// neue Zustände
|
// neue Zustände
|
||||||
let mut all = vec![];
|
let mut all = vec![];
|
||||||
|
|
||||||
@ -165,8 +149,6 @@ fn main() {
|
|||||||
// Lisa könnte zu dieser Ecke rennen
|
// Lisa könnte zu dieser Ecke rennen
|
||||||
let total_distance = last.total_distance + distance(last.pos, next);
|
let total_distance = last.total_distance + distance(last.pos, next);
|
||||||
let bus = best_bus_pos(&opt, total_distance, next);
|
let bus = best_bus_pos(&opt, total_distance, next);
|
||||||
// theoretisch möglicher Startzeitpunkt ist nach bestem gefundenem Startzeitpunkt
|
|
||||||
if bus > best_bus {
|
|
||||||
let mut route = state.clone();
|
let mut route = state.clone();
|
||||||
route.insert(
|
route.insert(
|
||||||
0,
|
0,
|
||||||
@ -176,8 +158,8 @@ fn main() {
|
|||||||
total_distance,
|
total_distance,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
all.push(route);
|
assert!(last.bus >= bus);
|
||||||
}
|
all.push((route, ((last.bus - bus) * 1000.0) as u32));
|
||||||
}
|
}
|
||||||
// versuche, direkt zum Bus zu gehen
|
// versuche, direkt zum Bus zu gehen
|
||||||
// Lisa trifft Bus in einem bestimmten Winkel (meist 60°)
|
// Lisa trifft Bus in einem bestimmten Winkel (meist 60°)
|
||||||
@ -191,8 +173,6 @@ fn main() {
|
|||||||
let line_length = line.end_point().euclidean_distance(&line.start_point());
|
let line_length = line.end_point().euclidean_distance(&line.start_point());
|
||||||
let total_distance = last.total_distance + line_length;
|
let total_distance = last.total_distance + line_length;
|
||||||
let bus = best_bus_pos(&opt, total_distance, line.end_point());
|
let bus = best_bus_pos(&opt, total_distance, line.end_point());
|
||||||
if bus > best_bus {
|
|
||||||
// neue beste Wartezeit
|
|
||||||
let mut route = state.clone();
|
let mut route = state.clone();
|
||||||
route.insert(
|
route.insert(
|
||||||
0,
|
0,
|
||||||
@ -202,43 +182,18 @@ fn main() {
|
|||||||
total_distance,
|
total_distance,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// Verbesserung anzeigen
|
all.push((route, 0));
|
||||||
eprintln!(
|
|
||||||
"Verbesserung: {:?} ({:?}+ Möglichkeiten verbleiben)",
|
|
||||||
calc_time(total_distance, line.end.y),
|
|
||||||
states.len()
|
|
||||||
);
|
|
||||||
best = route;
|
|
||||||
best.reverse();
|
|
||||||
best_bus = bus;
|
|
||||||
if opt.save_intermediates {
|
|
||||||
// Zwischenlösung als SVG speichern
|
|
||||||
display::save_svg(
|
|
||||||
opt,
|
|
||||||
&format!("{}{}.svg", save_prefix, save_counter),
|
|
||||||
house,
|
|
||||||
&polys,
|
|
||||||
&best,
|
|
||||||
);
|
|
||||||
save_counter += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// neu gefundene Routen speichern
|
// neu gefundene Routen speichern
|
||||||
// -> automatisch im BinaryHeap einsortiert
|
all
|
||||||
states.extend(all);
|
},
|
||||||
let end = Instant::now();
|
|node| node[0].pos.x() == 0.0,
|
||||||
total_time += end - start;
|
)
|
||||||
iterations += 1;
|
.map(|x| (x.0).last().cloned())
|
||||||
}
|
.unwrap()
|
||||||
if opt.debug {
|
.unwrap();
|
||||||
// Performanz-Schätzung anzeigen
|
best.reverse();
|
||||||
eprintln!(
|
|
||||||
"DEBUG: {}us/Iteration mit {} Iterationen",
|
|
||||||
total_time.as_micros() / iterations,
|
|
||||||
iterations
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// beste Lösung ausgeben
|
// beste Lösung ausgeben
|
||||||
eprintln!("Finale Lösung:");
|
eprintln!("Finale Lösung:");
|
||||||
let route = best;
|
let route = best;
|
||||||
|
Loading…
Reference in New Issue
Block a user