macro_rules ! input { ( source = $ s : expr , $ ( $ r : tt ) * ) => { let mut iter = $ s . split_whitespace ( ) ; input_inner ! { iter , $ ( $ r ) * } } ; ( iter = $ iter : ident , $ ( $ r : tt ) * ) => { let s = { use std :: io :: Read ; let mut s = String :: new ( ) ; std :: io :: stdin ( ) . read_to_string ( & mut s ) . unwrap ( ) ; s } ; let mut $ iter = s . split_whitespace ( ) ; input_inner ! { $ iter , $ ( $ r ) * } } ; ( $ ( $ r : tt ) * ) => { let s = { use std :: io :: Read ; let mut s = String :: new ( ) ; std :: io :: stdin ( ) . read_to_string ( & mut s ) . unwrap ( ) ; s } ; let mut iter = s . split_whitespace ( ) ; input_inner ! { iter , $ ( $ r ) * } } ; }
macro_rules ! input_inner { ( $ iter : expr ) => { } ; ( $ iter : expr , ) => { } ; ( $ iter : expr , mut $ var : ident : $ t : tt $ ( $ r : tt ) * ) => { let mut $ var = read_value ! ( $ iter , $ t ) ; input_inner ! { $ iter $ ( $ r ) * } } ; ( $ iter : expr , mut $ var : ident $ ( $ r : tt ) * ) => { input_inner ! { $ iter , mut $ var : usize $ ( $ r ) * } } ; ( $ iter : expr , $ var : ident : $ t : tt $ ( $ r : tt ) * ) => { let $ var = read_value ! ( $ iter , $ t ) ; input_inner ! { $ iter $ ( $ r ) * } } ; ( $ iter : expr , $ var : ident $ ( $ r : tt ) * ) => { input_inner ! { $ iter , $ var : usize $ ( $ r ) * } } ; }
macro_rules ! read_value { ( $ iter : expr , ( $ ( $ t : tt ) ,* ) ) => { ( $ ( read_value ! ( $ iter , $ t ) ) ,* ) } ; ( $ iter : expr , [ $ t : tt ; $ len : expr ] ) => { ( 0 ..$ len ) . map ( | _ | read_value ! ( $ iter , $ t ) ) . collect ::< Vec < _ >> ( ) } ; ( $ iter : expr , { chars : $ base : expr } ) => { read_value ! ( $ iter , String ) . chars ( ) . map ( | c | ( c as u8 - $ base as u8 ) as usize ) . collect ::< Vec < usize >> ( ) } ; ( $ iter : expr , { char : $ base : expr } ) => { read_value ! ( $ iter , { chars : $ base } ) [ 0 ] } ; ( $ iter : expr , chars ) => { read_value ! ( $ iter , String ) . chars ( ) . collect ::< Vec < char >> ( ) } ; ( $ iter : expr , char ) => { read_value ! ( $ iter , chars ) [ 0 ] } ; ( $ iter : expr , usize1 ) => { read_value ! ( $ iter , usize ) - 1 } ; ( $ iter : expr , $ t : ty ) => { $ iter . next ( ) . unwrap ( ) . parse ::<$ t > ( ) . unwrap ( ) } ; }
const MOD: i64 = 998244353;
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Modi64(pub i64);
impl Modi64 {
    #[inline]
    pub fn new(i: i64) -> Modi64 {
        Modi64(Modi64::modulo(i, MOD))
    }
    #[inline]
    pub fn modulo(i: i64, m: i64) -> i64 {
        if i < 0 {
            i % m + m
        } else {
            i % m
        }
    }
    pub fn pow(self, y: i64) -> Modi64 {
        let mut y = Modi64(Modi64::modulo(y, MOD - 1));
        let mut x = 1.into();
        let mut base = self;
        while y.0 > 0 {
            if y.0 & 1 == 1 {
                x *= base;
            }
            base *= base;
            y = Modi64(y.0 >> 1);
        }
        x
    }
    #[inline]
    pub fn inv(self) -> Modi64 {
        self.pow(MOD - 2)
    }
}
impl From<i64> for Modi64 {
    fn from(i: i64) -> Modi64 {
        Modi64::new(i)
    }
}
impl std::ops::Add for Modi64 {
    type Output = Modi64;
    fn add(self, rhs: Modi64) -> Modi64 {
        (self.0 + rhs.0).into()
    }
}
impl std::ops::AddAssign for Modi64 {
    fn add_assign(&mut self, rhs: Modi64) {
        *self = *self + rhs;
    }
}
impl std::ops::Sub for Modi64 {
    type Output = Modi64;
    fn sub(self, rhs: Modi64) -> Modi64 {
        (self.0 - rhs.0).into()
    }
}
impl std::ops::SubAssign for Modi64 {
    fn sub_assign(&mut self, rhs: Modi64) {
        *self = *self - rhs;
    }
}
impl std::ops::Mul for Modi64 {
    type Output = Modi64;
    fn mul(self, rhs: Modi64) -> Modi64 {
        (self.0 * rhs.0).into()
    }
}
impl std::ops::MulAssign for Modi64 {
    fn mul_assign(&mut self, rhs: Modi64) {
        *self = *self * rhs;
    }
}
impl std::ops::Div for Modi64 {
    type Output = Modi64;
    fn div(self, rhs: Modi64) -> Modi64 {
        self * rhs.inv()
    }
}
impl std::ops::DivAssign for Modi64 {
    fn div_assign(&mut self, rhs: Modi64) {
        *self = *self / rhs;
    }
}
impl std::ops::Neg for Modi64 {
    type Output = Modi64;
    fn neg(self) -> Modi64 {
        (-self.0).into()
    }
}
impl std::iter::Sum for Modi64 {
    fn sum<I: Iterator<Item = Modi64>>(iter: I) -> Modi64 {
        iter.fold(0.into(), std::ops::Add::add)
    }
}
impl<'a> std::iter::Sum<&'a Modi64> for Modi64 {
    fn sum<I: Iterator<Item = &'a Modi64>>(iter: I) -> Modi64 {
        iter.fold(0.into(), |x, &y| x + y)
    }
}
impl std::fmt::Display for Modi64 {
    fn fmt<'a>(&self, f: &mut std::fmt::Formatter<'a>) -> Result<(), std::fmt::Error> {
        write!(f, "{}", self.0)
    }
}
pub trait Bisect: Copy {
    fn halve(self, other: Self) -> Self;
    fn section_end(self, other: Self) -> bool;
}
impl Bisect for usize {
    fn halve(self, other: Self) -> Self {
        if self > other {
            other + (self - other) / 2
        } else {
            self + (other - self) / 2
        }
    }
    fn section_end(self, other: Self) -> bool {
        (if self > other {
            self - other
        } else {
            other - self
        }) <= 1
    }
}
impl Bisect for i64 {
    fn halve(self, other: Self) -> Self {
        (self + other) / 2
    }
    fn section_end(self, other: Self) -> bool {
        (self - other).abs() <= 1
    }
}
impl Bisect for f64 {
    fn halve(self, other: Self) -> Self {
        (self + other) / 2.
    }
    fn section_end(self, other: Self) -> bool {
        (self - other).abs() <= 1e-8
    }
}
pub fn binary_search<T: Bisect, F: Fn(T) -> bool>(f: F, ok: T, err: T) -> T {
    let mut ok = ok;
    let mut err = err;
    while !ok.section_end(err) {
        let m = ok.halve(err);
        if f(m) {
            ok = m;
        } else {
            err = m;
        }
    }
    ok
}
pub fn lower_bound<T: Bisect + Ord>(v: &[T], x: T) -> usize {
    binary_search(|i| v[i as usize] >= x, v.len() as i64, -1) as usize
}
pub fn upper_bound<T: Bisect + Ord>(v: &[T], x: T) -> usize {
    binary_search(|i| v[i as usize] > x, v.len() as i64, -1) as usize
}
pub struct Compress<T: Ord> {
    v: Vec<T>,
}
impl<T: Bisect + Ord> Compress<T> {
    pub fn get(&self, x: T) -> usize {
        lower_bound(&self.v, x)
    }
}
impl<T: Ord> std::iter::FromIterator<T> for Compress<T> {
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Compress<T> {
        use std::collections::BTreeSet;
        let v = iter
            .into_iter()
            .collect::<BTreeSet<T>>()
            .into_iter()
            .collect::<Vec<T>>();
        Compress { v: v }
    }
}
#[inline]
fn rmb(k: usize) -> usize {
    (k as isize & -(k as isize)) as usize
}
pub trait Monoid {
    type T: Clone + PartialEq;
    fn mempty(&self) -> Self::T;
    fn mappend(&self, x: &Self::T, y: &Self::T) -> Self::T;
}
pub struct BinaryIndexedTree<M: Monoid> {
    bit: Vec<M::T>,
    m: M,
}
impl<M: Monoid> BinaryIndexedTree<M> {
    pub fn new(n: usize, m: M) -> BinaryIndexedTree<M> {
        let bit = vec![m.mempty(); n + 1];
        BinaryIndexedTree { bit: bit, m: m }
    }
    #[doc = " 1-indexed"]
    pub fn query(&self, k: usize) -> M::T {
        let mut res = self.m.mempty();
        let mut k = k;
        while k > 0 {
            res = self.m.mappend(&res, &self.bit[k]);
            k -= rmb(k);
        }
        res
    }
    #[doc = " 1-indexed"]
    pub fn update(&mut self, k: usize, x: M::T) {
        let mut k = k;
        while k < self.bit.len() {
            self.bit[k] = self.m.mappend(&self.bit[k], &x);
            k += rmb(k);
        }
    }
}
pub trait Group: Monoid {
    fn invert(&self, x: &Self::T) -> Self::T;
}
impl<G: Group> BinaryIndexedTree<G> {
    #[doc = " 0-indexed [l, r)"]
    pub fn query_section(&self, l: usize, r: usize) -> G::T {
        self.m
            .mappend(&self.m.invert(&self.query(l)), &self.query(r))
    }
    #[doc = " 1-indexed"]
    pub fn get_val(&self, k: usize) -> G::T {
        self.query_section(k - 1, k)
    }
    #[doc = " 1-indexed"]
    pub fn set_val(&mut self, k: usize, x: G::T) {
        let y = self.m.invert(&self.get_val(k));
        let z = self.m.mappend(&y, &x);
        self.update(k, z);
    }
}
pub struct AnyGroup<T: Clone, F: Fn(&T, &T) -> T, G: Fn(&T) -> T> {
    id: T,
    op: F,
    inv: G,
}
impl<T: Clone, F: Fn(&T, &T) -> T, G: Fn(&T) -> T> AnyGroup<T, F, G> {
    pub fn new(id: T, op: F, inv: G) -> AnyGroup<T, F, G> {
        AnyGroup {
            id: id,
            op: op,
            inv: inv,
        }
    }
}
impl<T: Clone + PartialEq, F: Fn(&T, &T) -> T, G: Fn(&T) -> T> Monoid for AnyGroup<T, F, G> {
    type T = T;
    fn mempty(&self) -> T {
        self.id.clone()
    }
    fn mappend(&self, x: &T, y: &T) -> T {
        (self.op)(x, y)
    }
}
impl<T: Clone + PartialEq, F: Fn(&T, &T) -> T, G: Fn(&T) -> T> Group for AnyGroup<T, F, G> {
    fn invert(&self, x: &T) -> T {
        (self.inv)(x)
    }
}
fn main() {
    input! { n, mut xy: [(i64, i64); n] };
    xy.sort();
    let xzip = xy.iter().map(|&(x, _y)| x).collect::<Compress<_>>();
    let yzip = xy.iter().map(|&(_x, y)| y).collect::<Compress<_>>();
    let mut xs = xy.iter().map(|&(x, _y)| x).collect::<Vec<_>>();
    let mut ys = xy.iter().map(|&(_x, y)| y).collect::<Vec<_>>();
    xs.sort();
    ys.sort();
    let mut tab = vec![Modi64(0); n + 1];
    for i in 0..n {
        tab[i + 1] = tab[i] * 2.into() + 1.into();
    }
    let mut xcnt = vec![vec![]; n];
    for &(x, y) in xy.iter() {
        let (a, b) = (xzip.get(x), yzip.get(y));
        xcnt[a].push(b);
    }
    let mut ans = tab[n] * (n as i64).into();
    let mut bit = BinaryIndexedTree::new(n, AnyGroup::new(0, |&x, &y| x + y, |&x| -x));
    for a in 0..n {
        for &b in &xcnt[a] {
            ans += tab[bit.query_section(0, b) as usize];
            ans += tab[bit.query_section(b + 1, n) as usize];
        }
        for &b in &xcnt[a] {
            bit.update(b + 1, 1);
        }
    }
    let mut bit = BinaryIndexedTree::new(n, AnyGroup::new(0, |&x, &y| x + y, |&x| -x));
    for a in (0..n).rev() {
        for &b in &xcnt[a] {
            ans += tab[bit.query_section(0, b) as usize];
            ans += tab[bit.query_section(b + 1, n) as usize];
        }
        for &b in &xcnt[a] {
            bit.update(b + 1, 1);
        }
    }
    for &(x, y) in xy.iter() {
        let (xlb, xub) = (lower_bound(&xs, x), upper_bound(&xs, x));
        let (ylb, yub) = (lower_bound(&ys, y), upper_bound(&ys, y));
        ans -= tab[xlb];
        ans -= tab[n - xub];
        ans -= tab[ylb];
        ans -= tab[n - yub];
    }
    println!("{}", ans);
}


Measured memory_bytes: 35028