-
Notifications
You must be signed in to change notification settings - Fork 29
Add maxflow #24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add maxflow #24
Changes from all commits
1ff54bd
d4b6324
91ac693
67a48d0
ae780c9
693cfdb
55c0d81
a9bb6e8
43dfa84
b9ed34c
abbd181
a793068
51a9c98
c05ac18
7365855
125fc64
1c8d2ab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
use ac_library_rs::MfGraph; | ||
use std::io::Read; | ||
|
||
#[allow(clippy::many_single_char_names)] | ||
#[allow(clippy::needless_range_loop)] | ||
fn main() { | ||
const N: usize = 128; | ||
|
||
let mut buf = String::new(); | ||
std::io::stdin().read_to_string(&mut buf).unwrap(); | ||
let mut input = buf.split_whitespace(); | ||
|
||
let n: usize = input.next().unwrap().parse().unwrap(); | ||
let m: usize = input.next().unwrap().parse().unwrap(); | ||
let mut s = vec![vec![false; N]; N]; | ||
for i in 1..=n { | ||
for (j, c) in (1..=m).zip(input.next().unwrap().chars()) { | ||
s[i][j] = c == '.'; | ||
} | ||
} | ||
|
||
let dy = [1, 0, -1, 0]; | ||
let dx = [0, 1, 0, -1]; | ||
let c = "v>^<".chars().collect::<Vec<_>>(); | ||
let mut graph = MfGraph::<i32>::new(128 * 128); | ||
|
||
for i in 1..=n { | ||
for j in 1..=m { | ||
if (i + j) % 2 == 0 { | ||
graph.add_edge(0, i * N + j, 1); | ||
for (dy, dx) in dy.iter().zip(dx.iter()) { | ||
let k = (i as i32 + dy) as usize; | ||
let l = (j as i32 + dx) as usize; | ||
if s[i][j] && s[k][l] { | ||
graph.add_edge(i * N + j, k * N + l, 1); | ||
} | ||
} | ||
} else { | ||
graph.add_edge(i * N + j, 1, 1); | ||
} | ||
} | ||
} | ||
println!("{}", graph.flow(0, 1)); | ||
let mut res: Vec<Vec<_>> = (1..=n) | ||
.map(|i| (1..=m).map(|j| if s[i][j] { '.' } else { '#' }).collect()) | ||
.collect(); | ||
for e in graph.edges() { | ||
let i = e.from / N; | ||
let j = e.from % N; | ||
let k = e.to / N; | ||
let l = e.to % N; | ||
if e.flow == 1 { | ||
for h in 0..4 { | ||
if i as i32 + dy[h] == k as i32 && j as i32 + dx[h] == l as i32 { | ||
res[i - 1][j - 1] = c[h]; | ||
res[k - 1][l - 1] = c[h ^ 2]; | ||
} | ||
} | ||
} | ||
} | ||
for s in res { | ||
println!("{}", s.iter().collect::<String>()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1 +1,325 @@ | ||||||||||||||
#![allow(dead_code)] | ||||||||||||||
use crate::internal_queue::SimpleQueue; | ||||||||||||||
use crate::internal_type_traits::Integral; | ||||||||||||||
use std::cmp::min; | ||||||||||||||
use std::iter; | ||||||||||||||
|
||||||||||||||
impl<Cap> MfGraph<Cap> | ||||||||||||||
where | ||||||||||||||
Cap: Integral, | ||||||||||||||
{ | ||||||||||||||
pub fn new(n: usize) -> MfGraph<Cap> { | ||||||||||||||
MfGraph { | ||||||||||||||
_n: n, | ||||||||||||||
pos: Vec::new(), | ||||||||||||||
g: iter::repeat_with(Vec::new).take(n).collect(), | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
pub fn add_edge(&mut self, from: usize, to: usize, cap: Cap) -> usize { | ||||||||||||||
assert!(from < self._n); | ||||||||||||||
assert!(to < self._n); | ||||||||||||||
assert!(Cap::zero() <= cap); | ||||||||||||||
let m = self.pos.len(); | ||||||||||||||
self.pos.push((from, self.g[from].len())); | ||||||||||||||
let rev = self.g[to].len() + if from == to { 1 } else { 0 }; | ||||||||||||||
self.g[from].push(_Edge { to, rev, cap }); | ||||||||||||||
let rev = self.g[from].len() - 1; | ||||||||||||||
self.g[to].push(_Edge { | ||||||||||||||
to: from, | ||||||||||||||
rev, | ||||||||||||||
cap: Cap::zero(), | ||||||||||||||
}); | ||||||||||||||
m | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
#[derive(Debug, PartialEq, Eq)] | ||||||||||||||
pub struct Edge<Cap: Integral> { | ||||||||||||||
pub from: usize, | ||||||||||||||
pub to: usize, | ||||||||||||||
pub cap: Cap, | ||||||||||||||
pub flow: Cap, | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
impl<Cap> MfGraph<Cap> | ||||||||||||||
where | ||||||||||||||
Cap: Integral, | ||||||||||||||
{ | ||||||||||||||
pub fn get_edge(&self, i: usize) -> Edge<Cap> { | ||||||||||||||
let m = self.pos.len(); | ||||||||||||||
assert!(i < m); | ||||||||||||||
let _e = &self.g[self.pos[i].0][self.pos[i].1]; | ||||||||||||||
let _re = &self.g[_e.to][_e.rev]; | ||||||||||||||
Edge { | ||||||||||||||
from: self.pos[i].0, | ||||||||||||||
to: _e.to, | ||||||||||||||
cap: _e.cap + _re.cap, | ||||||||||||||
flow: _re.cap, | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
pub fn edges(&self) -> Vec<Edge<Cap>> { | ||||||||||||||
let m = self.pos.len(); | ||||||||||||||
(0..m).map(|i| self.get_edge(i)).collect() | ||||||||||||||
} | ||||||||||||||
pub fn change_edge(&mut self, i: usize, new_cap: Cap, new_flow: Cap) { | ||||||||||||||
let m = self.pos.len(); | ||||||||||||||
assert!(i < m); | ||||||||||||||
assert!(Cap::zero() <= new_flow && new_flow <= new_cap); | ||||||||||||||
let (to, rev) = { | ||||||||||||||
let _e = &mut self.g[self.pos[i].0][self.pos[i].1]; | ||||||||||||||
_e.cap = new_cap - new_flow; | ||||||||||||||
(_e.to, _e.rev) | ||||||||||||||
}; | ||||||||||||||
let _re = &mut self.g[to][rev]; | ||||||||||||||
_re.cap = new_flow; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
/// `s != t` must hold, otherwise it panics. | ||||||||||||||
pub fn flow(&mut self, s: usize, t: usize) -> Cap { | ||||||||||||||
self.flow_with_capacity(s, t, Cap::max_value()) | ||||||||||||||
} | ||||||||||||||
/// # Parameters | ||||||||||||||
/// * `s != t` must hold, otherwise it panics. | ||||||||||||||
/// * `flow_limit >= 0` | ||||||||||||||
pub fn flow_with_capacity(&mut self, s: usize, t: usize, flow_limit: Cap) -> Cap { | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the reason of this naming? It make sense to name this a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have an image of adding new edge from virtual vertex |
||||||||||||||
let n_ = self._n; | ||||||||||||||
assert!(s < n_); | ||||||||||||||
assert!(t < n_); | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I should not add this assertion, because the original documentation does not say it disallows the same vertices, and by definition we can return a valid value There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By definition in the document appendix.html, we should return 0 I believe, which is very confusing as you might have noticed.
This implies the sum of g(v, f) over all vertices is zero.
Since s = t, this also implies g(t, f) = g(t, f'). (∵ everywhere except t is fixed, and the sum is fixed)
Since g(t, f) = g(t, f'), the returned value should be 0. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I finally understood. It seems to be true that it should return 0. Doesn't the current code? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried the original library with a simple example and it returned There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't return 0 if the limit wasn't 0, since #[cfg(test)]
mod test {
use super::*;
#[test]
fn test() {
let mut g = MfGraph::new(3);
assert_eq!(0, g.flow_with_capacity(0, 0, 10));
}
} fails with
If you formulate the max flow problem in LP, the primal would be UNBOUND and the dual would be INFEASIBLE. So it also makes sense to return the infinity. Since there are multiple way of interpreting the meaning of max flow for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reported in atcoder/ac-library#5 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ACL fixed it :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Plus
Suggested change
maybe? |
||||||||||||||
// By the definition of max flow in appendix.html, this function should return 0 | ||||||||||||||
// when the same vertices are provided. On the other hand, it is reasonable to | ||||||||||||||
// return infinity-like value too, which is what the original implementation | ||||||||||||||
// (and this implementation without the following assertion) does. | ||||||||||||||
// Since either return value is confusing, we'd rather deny the parameters | ||||||||||||||
// of the two same vertices. | ||||||||||||||
// For more details, see https://github.com/rust-lang-ja/ac-library-rs/pull/24#discussion_r485343451 | ||||||||||||||
// and https://github.com/atcoder/ac-library/issues/5 . | ||||||||||||||
assert_ne!(s, t); | ||||||||||||||
// Additional constraint | ||||||||||||||
assert!(Cap::zero() <= flow_limit); | ||||||||||||||
|
||||||||||||||
let mut calc = FlowCalculator { | ||||||||||||||
graph: self, | ||||||||||||||
s, | ||||||||||||||
t, | ||||||||||||||
flow_limit, | ||||||||||||||
level: vec![0; n_], | ||||||||||||||
iter: vec![0; n_], | ||||||||||||||
que: SimpleQueue::default(), | ||||||||||||||
}; | ||||||||||||||
|
||||||||||||||
let mut flow = Cap::zero(); | ||||||||||||||
while flow < flow_limit { | ||||||||||||||
calc.bfs(); | ||||||||||||||
if calc.level[t] == -1 { | ||||||||||||||
break; | ||||||||||||||
} | ||||||||||||||
calc.iter.iter_mut().for_each(|e| *e = 0); | ||||||||||||||
while flow < flow_limit { | ||||||||||||||
let f = calc.dfs(t, flow_limit - flow); | ||||||||||||||
if f == Cap::zero() { | ||||||||||||||
break; | ||||||||||||||
} | ||||||||||||||
flow += f; | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
flow | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
pub fn min_cut(&self, s: usize) -> Vec<bool> { | ||||||||||||||
let mut visited = vec![false; self._n]; | ||||||||||||||
let mut que = SimpleQueue::default(); | ||||||||||||||
que.push(s); | ||||||||||||||
while !que.empty() { | ||||||||||||||
let &p = que.front().unwrap(); | ||||||||||||||
que.pop(); | ||||||||||||||
visited[p] = true; | ||||||||||||||
for e in &self.g[p] { | ||||||||||||||
if e.cap != Cap::zero() && !visited[e.to] { | ||||||||||||||
visited[e.to] = true; | ||||||||||||||
que.push(e.to); | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
visited | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
struct FlowCalculator<'a, Cap> { | ||||||||||||||
graph: &'a mut MfGraph<Cap>, | ||||||||||||||
s: usize, | ||||||||||||||
t: usize, | ||||||||||||||
flow_limit: Cap, | ||||||||||||||
level: Vec<i32>, | ||||||||||||||
iter: Vec<usize>, | ||||||||||||||
que: SimpleQueue<usize>, | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
impl<Cap> FlowCalculator<'_, Cap> | ||||||||||||||
where | ||||||||||||||
Cap: Integral, | ||||||||||||||
{ | ||||||||||||||
fn bfs(&mut self) { | ||||||||||||||
self.level.iter_mut().for_each(|e| *e = -1); | ||||||||||||||
self.level[self.s] = 0; | ||||||||||||||
self.que.clear(); | ||||||||||||||
self.que.push(self.s); | ||||||||||||||
while !self.que.empty() { | ||||||||||||||
let v = *self.que.front().unwrap(); | ||||||||||||||
self.que.pop(); | ||||||||||||||
for e in &self.graph.g[v] { | ||||||||||||||
if e.cap == Cap::zero() || self.level[e.to] >= 0 { | ||||||||||||||
continue; | ||||||||||||||
} | ||||||||||||||
self.level[e.to] = self.level[v] + 1; | ||||||||||||||
if e.to == self.t { | ||||||||||||||
return; | ||||||||||||||
} | ||||||||||||||
self.que.push(e.to); | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
fn dfs(&mut self, v: usize, up: Cap) -> Cap { | ||||||||||||||
if v == self.s { | ||||||||||||||
return up; | ||||||||||||||
} | ||||||||||||||
let mut res = Cap::zero(); | ||||||||||||||
let level_v = self.level[v]; | ||||||||||||||
for i in self.iter[v]..self.graph.g[v].len() { | ||||||||||||||
self.iter[v] = i; | ||||||||||||||
let &_Edge { | ||||||||||||||
to: e_to, | ||||||||||||||
rev: e_rev, | ||||||||||||||
.. | ||||||||||||||
} = &self.graph.g[v][i]; | ||||||||||||||
if level_v <= self.level[e_to] || self.graph.g[e_to][e_rev].cap == Cap::zero() { | ||||||||||||||
continue; | ||||||||||||||
} | ||||||||||||||
let d = self.dfs(e_to, min(up - res, self.graph.g[e_to][e_rev].cap)); | ||||||||||||||
if d <= Cap::zero() { | ||||||||||||||
continue; | ||||||||||||||
} | ||||||||||||||
self.graph.g[v][i].cap += d; | ||||||||||||||
self.graph.g[e_to][e_rev].cap -= d; | ||||||||||||||
res += d; | ||||||||||||||
if res == up { | ||||||||||||||
break; | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This is an important bit of the algorithm that we should not omit. Alternatively we can do
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right. It is difficult to translate C++ |
||||||||||||||
self.iter[v] = self.graph.g[v].len(); | ||||||||||||||
res | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
#[derive(Default)] | ||||||||||||||
pub struct MfGraph<Cap> { | ||||||||||||||
_n: usize, | ||||||||||||||
pos: Vec<(usize, usize)>, | ||||||||||||||
g: Vec<Vec<_Edge<Cap>>>, | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
struct _Edge<Cap> { | ||||||||||||||
to: usize, | ||||||||||||||
rev: usize, | ||||||||||||||
cap: Cap, | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
#[cfg(test)] | ||||||||||||||
mod test { | ||||||||||||||
use crate::{Edge, MfGraph}; | ||||||||||||||
|
||||||||||||||
#[test] | ||||||||||||||
fn test_max_flow_wikipedia() { | ||||||||||||||
// From https://commons.wikimedia.org/wiki/File:Min_cut.png | ||||||||||||||
// Under CC BY-SA 3.0 https://creativecommons.org/licenses/by-sa/3.0/deed.en | ||||||||||||||
let mut graph = MfGraph::new(6); | ||||||||||||||
assert_eq!(graph.add_edge(0, 1, 3), 0); | ||||||||||||||
assert_eq!(graph.add_edge(0, 2, 3), 1); | ||||||||||||||
assert_eq!(graph.add_edge(1, 2, 2), 2); | ||||||||||||||
assert_eq!(graph.add_edge(1, 3, 3), 3); | ||||||||||||||
assert_eq!(graph.add_edge(2, 4, 2), 4); | ||||||||||||||
assert_eq!(graph.add_edge(3, 4, 4), 5); | ||||||||||||||
assert_eq!(graph.add_edge(3, 5, 2), 6); | ||||||||||||||
assert_eq!(graph.add_edge(4, 5, 3), 7); | ||||||||||||||
|
||||||||||||||
assert_eq!(graph.flow(0, 5), 5); | ||||||||||||||
|
||||||||||||||
let edges = graph.edges(); | ||||||||||||||
{ | ||||||||||||||
#[rustfmt::skip] | ||||||||||||||
assert_eq!( | ||||||||||||||
edges, | ||||||||||||||
vec![ | ||||||||||||||
Edge { from: 0, to: 1, cap: 3, flow: 3 }, | ||||||||||||||
Edge { from: 0, to: 2, cap: 3, flow: 2 }, | ||||||||||||||
Edge { from: 1, to: 2, cap: 2, flow: 0 }, | ||||||||||||||
Edge { from: 1, to: 3, cap: 3, flow: 3 }, | ||||||||||||||
Edge { from: 2, to: 4, cap: 2, flow: 2 }, | ||||||||||||||
Edge { from: 3, to: 4, cap: 4, flow: 1 }, | ||||||||||||||
Edge { from: 3, to: 5, cap: 2, flow: 2 }, | ||||||||||||||
Edge { from: 4, to: 5, cap: 3, flow: 3 }, | ||||||||||||||
] | ||||||||||||||
); | ||||||||||||||
} | ||||||||||||||
assert_eq!( | ||||||||||||||
graph.min_cut(0), | ||||||||||||||
vec![true, false, true, false, false, false] | ||||||||||||||
); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
#[test] | ||||||||||||||
fn test_max_flow_wikipedia_multiple_edges() { | ||||||||||||||
// From https://commons.wikimedia.org/wiki/File:Min_cut.png | ||||||||||||||
// Under CC BY-SA 3.0 https://creativecommons.org/licenses/by-sa/3.0/deed.en | ||||||||||||||
let mut graph = MfGraph::new(6); | ||||||||||||||
for &(u, v, c) in &[ | ||||||||||||||
(0, 1, 3), | ||||||||||||||
(0, 2, 3), | ||||||||||||||
(1, 2, 2), | ||||||||||||||
(1, 3, 3), | ||||||||||||||
(2, 4, 2), | ||||||||||||||
(3, 4, 4), | ||||||||||||||
(3, 5, 2), | ||||||||||||||
(4, 5, 3), | ||||||||||||||
] { | ||||||||||||||
for _ in 0..c { | ||||||||||||||
graph.add_edge(u, v, 1); | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
assert_eq!(graph.flow(0, 5), 5); | ||||||||||||||
assert_eq!( | ||||||||||||||
graph.min_cut(0), | ||||||||||||||
vec![true, false, true, false, false, false] | ||||||||||||||
); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
#[test] | ||||||||||||||
#[allow(clippy::many_single_char_names)] | ||||||||||||||
fn test_max_flow_misawa() { | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You might have done already, but verified locally that it gonna take very long with n=20 and take forever with n=100 when we deleted L:189 and L:209 as intended :) |
||||||||||||||
// Originally by @MiSawa | ||||||||||||||
// From https://gist.github.com/MiSawa/47b1d99c372daffb6891662db1a2b686 | ||||||||||||||
let n = 100; | ||||||||||||||
|
||||||||||||||
let mut graph = MfGraph::new((n + 1) * 2 + 5); | ||||||||||||||
let (s, a, b, c, t) = (0, 1, 2, 3, 4); | ||||||||||||||
graph.add_edge(s, a, 1); | ||||||||||||||
graph.add_edge(s, b, 2); | ||||||||||||||
graph.add_edge(b, a, 2); | ||||||||||||||
graph.add_edge(c, t, 2); | ||||||||||||||
for i in 0..n { | ||||||||||||||
let i = 2 * i + 5; | ||||||||||||||
for j in 0..2 { | ||||||||||||||
for k in 2..4 { | ||||||||||||||
graph.add_edge(i + j, i + k, 3); | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
for j in 0..2 { | ||||||||||||||
graph.add_edge(a, 5 + j, 3); | ||||||||||||||
graph.add_edge(2 * n + 5 + j, c, 3); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
assert_eq!(graph.flow(s, t), 2); | ||||||||||||||
} | ||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually the algorithm doesn't rely on the integrality.... Though it does rely on
Ord
. Not sure what we should do.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One possible way is to define a trait dedicated for max-flow capacities, which is my previous implementation. Refer to this commit to see the differences. However this is a bit like a repetition of
Integral
trait ininternal_type_traits
module, that's why I simply reused it.At least the original library guarantees only
int
andll
, I guess this is already sufficient.By the way, can this implementation be applied to floating-point capacity? I mean, don't we need some additional treatment of precision errors?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question!
I believe so long as min(x, y) in {x, y} and x-x = 0, the algorithm work fine-ish. e.g. for floating point numbers without NaN / inf, polynomial, etc.
I said fine-ish since although the time complexity doesn't get worse, there are errors that are unavoidable without using multi-precision floating point number. Guaranteeing the accuracy of the returned flow value and flows on edges would be hard. Farthemore since cut is discrete, the min-cut verticis can be different from the correct one although the cut value should be similar.
In competitive programming, I think I have seen some problems that uses polynomial flows (or big-M instead) and floating point numbers (came from binary/ternary search, can be transformed to a search on rational numbers though require a lot more coding).
Though maybe it will be very rare in AtCoder at least, since ACL doesn't support them.