use std::io::prelude::*;

fn input<T: std::str::FromStr>() -> T {
    let stdin = std::io::stdin();
    let token: String = stdin
        .lock()
        .bytes()
        .map(|c| c.unwrap() as char)
        .skip_while(|c| c.is_whitespace())
        .take_while(|c| !c.is_whitespace())
        .collect();
    token.parse().ok().unwrap()
}

fn main() {
    let n: usize = input();
    let mut xy: Vec<(i32, i32)> = (0..n).map(|_| (input(), input())).collect();
    xy.sort();

    let mut ys = vec![i32::min_value(), i32::max_value()];
    for &(_, y) in &xy {
        ys.push(y);
    }
    ys.sort();

    let mut num = vec![vec![0; 4]; n];

    let mut bit = FenwickTree::new(n);
    for i in 0..n {
        let (_, y) = xy[i];
        let y = ys.lower_bound(&y);
        num[i][2] = bit.sum(y - 1);
        num[i][1] = i - num[i][2];
        bit.add(y, 1);
    }

    bit = FenwickTree::new(n);
    for i in (0..n).rev() {
        let (_, y) = xy[i];
        let y = ys.lower_bound(&y);
        num[i][3] = bit.sum(y - 1);
        num[i][0] = (n - i - 1) - num[i][3];
        bit.add(y, 1);
    }

    let mut ans = 0i64;
    for i in 0..n {
        let mut a = vec![0; 4];
        for j in 0..4 {
            a[j] = pow(2, num[i][j] as i64);
        }
        let mut t = a[0] * a[1] % MOD * a[2] % MOD * a[3] % MOD;
        t += (a[0] - 1) * (a[2] - 1) % MOD * a[1] % MOD * a[3] % MOD;
        t += (a[1] - 1) * (a[3] - 1) % MOD * a[0] % MOD * a[2] % MOD;
        t -= (a[0] - 1) * (a[2] - 1) % MOD * (a[1] - 1) % MOD * (a[3] - 1) % MOD;
        ans += t % MOD;
    }

    println!("{}", ans % MOD);
}

const MOD: i64 = 998244353;

fn pow(x: i64, p: i64) -> i64 {
    if p == 0 {
        1
    } else if p % 2 == 0 {
        let t = pow(x, p / 2);
        t * t % MOD
    } else {
        let t = pow(x, p / 2);
        t * t % MOD * x % MOD
    }
}

trait LowerBound<T: Ord> {
    fn lower_bound_by<F: FnMut(&T) -> std::cmp::Ordering>(&self, f: F) -> usize;

    fn lower_bound(&self, x: &T) -> usize {
        self.lower_bound_by(|e| e.cmp(x))
    }

    fn lower_bound_by_key<U: Ord, F: FnMut(&T) -> U>(&self, x: &U, mut f: F) -> usize {
        self.lower_bound_by(|e| f(e).cmp(x))
    }
}

impl<T: Ord> LowerBound<T> for [T] {
    fn lower_bound_by<F: FnMut(&T) -> std::cmp::Ordering>(&self, mut f: F) -> usize {
        let mut l = -1 as i64;
        let mut r = self.len() as i64;
        while r - l > 1 {
            let m = ((l + r) / 2) as usize;
            if f(&self[m]) == std::cmp::Ordering::Less {
                l = m as i64;
            } else {
                r = m as i64;
            }
        }
        r as usize
    }
}

struct FenwickTree<T>(Vec<T>);

impl<T: Clone + Copy + Default + PartialOrd + std::ops::AddAssign + std::ops::SubAssign>
    FenwickTree<T>
{
    fn new(n: usize) -> FenwickTree<T> {
        FenwickTree(vec![T::default(); n + 1])
    }

    fn add(&mut self, i: usize, v: T) {
        let n = self.0.len() as i64;
        let mut i = i as i64;
        while i < n {
            self.0[i as usize] += v;
            i += i & -i;
        }
    }

    fn sum(&self, i: usize) -> T {
        let mut i = i as i64;
        let mut r = T::default();
        while i > 0 {
            r += self.0[i as usize];
            i -= i & -i;
        }
        r
    }
}


Measured memory_bytes: 22780