#![allow(non_snake_case)]
#![allow(unused_imports)]
#![allow(dead_code)]

use proconio::{input, fastout};
use proconio::marker::*;
use whiteread::parse_line;
use std::collections::*;
use num::*;
use num_traits::*;
use superslice::*;
use std::ops::*;

use itertools::Itertools;
use itertools_num::ItertoolsNum;

#[derive(Debug)]
pub struct ModInt<T> {
    v: T,
    m: T,
}
impl<T> ModInt<T>
where
    T: Copy,
{
    pub fn value(&self) -> T {
        self.v
    }
    pub fn modulo(&self) -> T {
        self.m
    }
}
impl<T> ModInt<T> {
    fn new_unchecked(v: T, modulo: T) -> Self {
        Self { v, m: modulo }
    }
}
impl<T> ModInt<T>
where
    T: Copy + RemAssign + PartialOrd,
{
    pub fn new(mut v: T, modulo: T) -> Self {
        if v >= modulo {
            v %= modulo;
        }
        Self::new_unchecked(v, modulo)
    }
}
impl<T> ModInt<T>
where
    T: Copy
        + Sub<Output = T>
        + ShrAssign
        + BitAnd<Output = T>
        + PartialEq
        + PartialOrd
        + Div<Output = T>
        + RemAssign,
    ModInt<T>: MulAssign,
{
    pub fn pow(self, e: T) -> Self {
        let zero = self.modulo() - self.modulo();
        let one = self.modulo() / self.modulo();
        let mut e = e;
        let mut result = Self::new_unchecked(one, self.modulo());
        let mut cur = self;
        while e > zero {
            if e & one == one {
                result *= cur;
            }
            e >>= one;
            cur *= cur;
        }
        result
    }
}
impl<T> Copy for ModInt<T> where T: Copy {}
impl<T> Clone for ModInt<T>
where
    T: Copy,
{
    fn clone(&self) -> Self {
        Self::new_unchecked(self.value(), self.modulo())
    }
}
impl<T> Add<T> for ModInt<T>
where
    T: AddAssign + SubAssign + RemAssign + Copy + PartialOrd,
{
    type Output = Self;
    fn add(self, mut rhs: T) -> Self::Output {
        if rhs >= self.modulo() {
            rhs %= self.modulo();
        }
        rhs += self.value();
        if rhs >= self.modulo() {
            rhs -= self.modulo();
        }
        Self::new_unchecked(rhs, self.modulo())
    }
}
impl<T> Sub<T> for ModInt<T>
where
    T: AddAssign + SubAssign + RemAssign + Copy + PartialOrd,
{
    type Output = Self;
    fn sub(self, mut rhs: T) -> Self::Output {
        if rhs >= self.modulo() {
            rhs %= self.modulo();
        }
        let mut result = self.value();
        result += self.modulo();
        result -= rhs;
        if result >= self.modulo() {
            result -= self.modulo();
        }
        Self::new_unchecked(result, self.modulo())
    }
}
impl<T> Mul<T> for ModInt<T>
where
    T: MulAssign + RemAssign + Copy + PartialOrd,
{
    type Output = Self;
    fn mul(self, mut rhs: T) -> Self::Output {
        if rhs >= self.modulo() {
            rhs %= self.modulo();
        }
        rhs *= self.value();
        rhs %= self.modulo();
        Self::new_unchecked(rhs, self.modulo())
    }
}
impl<T> Add<ModInt<T>> for ModInt<T>
where
    T: Copy,
    ModInt<T>: Add<T, Output = ModInt<T>>,
{
    type Output = Self;
    fn add(self, rhs: ModInt<T>) -> Self::Output {
        self + rhs.value()
    }
}
impl<T> Sub<ModInt<T>> for ModInt<T>
where
    T: Copy,
    ModInt<T>: Sub<T, Output = ModInt<T>>,
{
    type Output = Self;
    fn sub(self, rhs: ModInt<T>) -> Self::Output {
        self - rhs.value()
    }
}
impl<T> Mul<ModInt<T>> for ModInt<T>
where
    T: Copy,
    ModInt<T>: Mul<T, Output = ModInt<T>>,
{
    type Output = Self;
    fn mul(self, rhs: ModInt<T>) -> Self::Output {
        self * rhs.value()
    }
}
impl<T> Div<ModInt<T>> for ModInt<T>
where
    T: Copy,
    ModInt<T>: Div<T, Output = ModInt<T>>,
{
    type Output = Self;
    fn div(self, rhs: ModInt<T>) -> Self::Output {
        self / rhs.value()
    }
}
impl<T> AddAssign<T> for ModInt<T>
where
    T: Copy,
    ModInt<T>: Add<T, Output = ModInt<T>>,
{
    fn add_assign(&mut self, other: T) {
        *self = *self + other;
    }
}
impl<T> AddAssign<ModInt<T>> for ModInt<T>
where
    T: Copy,
    ModInt<T>: Add<ModInt<T>, Output = ModInt<T>>,
{
    fn add_assign(&mut self, other: ModInt<T>) {
        *self = *self + other;
    }
}
impl<T> SubAssign<T> for ModInt<T>
where
    T: Copy,
    ModInt<T>: Sub<T, Output = ModInt<T>>,
{
    fn sub_assign(&mut self, other: T) {
        *self = *self - other;
    }
}
impl<T> SubAssign<ModInt<T>> for ModInt<T>
where
    T: Copy,
    ModInt<T>: Sub<ModInt<T>, Output = ModInt<T>>,
{
    fn sub_assign(&mut self, other: ModInt<T>) {
        *self = *self - other;
    }
}
impl<T> DivAssign<T> for ModInt<T>
where
    T: Copy,
    ModInt<T>: Div<T, Output = ModInt<T>>,
{
    fn div_assign(&mut self, rhs: T) {
        *self = *self / rhs
    }
}
impl<T> DivAssign<ModInt<T>> for ModInt<T>
where
    T: Copy,
    ModInt<T>: Div<ModInt<T>, Output = ModInt<T>>,
{
    fn div_assign(&mut self, rhs: ModInt<T>) {
        *self = *self / rhs
    }
}
impl<T> MulAssign<T> for ModInt<T>
where
    T: Copy,
    ModInt<T>: Mul<T, Output = ModInt<T>>,
{
    fn mul_assign(&mut self, rhs: T) {
        *self = *self * rhs;
    }
}
impl<T> MulAssign<ModInt<T>> for ModInt<T>
where
    T: Copy,
    ModInt<T>: Mul<ModInt<T>, Output = ModInt<T>>,
{
    fn mul_assign(&mut self, rhs: ModInt<T>) {
        *self = *self * rhs;
    }
}
impl<T> Div<T> for ModInt<T>
where
    T: Copy
        + Add<Output = T>
        + Sub<Output = T>
        + Div<Output = T>
        + BitAnd<Output = T>
        + PartialEq
        + PartialOrd
        + SubAssign
        + ShrAssign
        + RemAssign
        + MulAssign,
{
    type Output = Self;
    fn div(self, mut rhs: T) -> Self::Output {
        if rhs >= self.modulo() {
            rhs %= self.modulo();
        }
        let one = self.modulo() / self.modulo();
        let two = one + one;
        self * Self::new_unchecked(rhs, self.modulo()).pow(self.modulo() - two)
    }
}

#[derive(Debug, Clone)]
pub struct Combination {
    fact_inv: Vec<usize>,
    inv: Vec<usize>,
    com: Option<Vec<usize>>,
    m: usize,
}
impl Combination {
    pub fn new(upper: usize, m: usize) -> Self {
        let mut fact_inv = vec![0; upper + 1];
        let mut inv = vec![0; upper + 1];
        fact_inv[0] = 1;
        fact_inv[1] = 1;
        inv[1] = 1;
        for i in 2..=upper {
            inv[i] = m - inv[m % i] * (m / i) % m;
            fact_inv[i] = fact_inv[i - 1] * inv[i] % m;
        }
        Self {
            fact_inv: fact_inv,
            inv: inv,
            com: None,
            m: m,
        }
    }
    pub fn fix_n(&mut self, n: usize) {
        let mut com = vec![0; n + 1];
        com[0] = 1;
        for i in 1..=n {
            com[i] = com[i - 1] * ((n - i + 1) * self.inv[i] % self.m) % self.m;
        }
        self.com = Some(com)
    }
    fn _calc_nck(&self, n: usize, k: usize) -> usize {
        if n < k {
            return 0;
        }
        let mut ans: usize = 1;
        let mut i = n;
        while n - k < i {
            ans *= i;
            ans %= self.m;
            i -= 1;
        }
        ans * self.fact_inv[k] % self.m
    }
    pub fn nck(&self, n: usize, k: usize) -> usize {
        match self.com.clone() {
            None => self._calc_nck(n, k),
            Some(x) => x[k],
        }
    }
    pub fn nhk(&self, n: usize, k: usize) -> usize {
        assert!(self.fact_inv.len() >= n + k - 1);
        self.nck(n + k - 1, k)
    }
}

#[fastout]
fn solve() {
    const MOD: usize = 1_000_000_007;
    const INF: usize = std::usize::MAX;
    
    input!{
        n: usize,
    }

    if n == 1 {
        println!("0");
    } else {
        let ans: ModInt<usize> = ModInt::new(10, MOD).pow(n) + ModInt::new(8, MOD).pow(n) - ModInt::new(9, MOD).pow(n) * 2usize;
        
        // if n > 2 {
        //     let comb = Combination::new(n-1, MOD);
        //     let c = comb.nck(n-1, 2);
        //     ans = ModInt::new(c, MOD) * ans * 2usize;
        // }
        println!("{}", ans.value())
    }
}

fn main() {
    solve()
}

#[cfg(test)]
mod test {
    use super::solve;
}


Measured memory_bytes: 2176