#include <bits/stdc++.h>

using namespace std;

#define SZ(x) (int)(x.size())
#define REP(i, n) for(int i=0;i<(n);++i)
#define FOR(i, a, b) for(int i=(a);i<(b);++i)
#define RREP(i, n) for(int i=(int)(n);i>=0;--i)
#define RFOR(i, a, b) for(int i=(int)(a);i>=(int)(b);--i)
#define ALL(a) (a).begin(),(a).end()
#define DUMP(x) cerr<<#x<<" = "<<(x)<<endl
#define DEBUG(x) cerr<<#x<<" = "<<(x)<<" (L"<<__LINE__<<")"<< endl;

using ll = long long;
using vi = vector<int>;
using vvi = vector<vi>;
using vll = vector<ll>;
using vvll = vector<vll>;
using P = pair<int, int>;

const double eps = 1e-8;
const ll MOD = 1000000007;
const int INF = INT_MAX / 2;
const ll LINF = LLONG_MAX / 2;

template <typename T1, typename T2>
bool chmax(T1 &a, const T2 &b) {
  if(a < b) {a = b; return true;}
  return false;
}

template <typename T1, typename T2>
bool chmin(T1 &a, const T2 &b) {
  if(a > b) {a = b; return true;}
  return false;
}

template<typename T1, typename T2>
ostream& operator<<(ostream &os, const pair<T1, T2> p) {
  os << p.first << ":" << p.second;
  return os;
}

template<class T>
ostream &operator<<(ostream &os, const vector<T> &v) {
  REP(i, SZ(v)) {
    if(i) os << " ";
    os << v[i];
  }
  return os;
}

template<typename M, typename OM = M>
struct LazySegmentTree {

  /**
  * @brief コンストラクタ. O(n)
  * @param[in] n 遅延セグ木のサイズ.
  * @param[in] f 要素モノイドの演算.
  * @param[in] g 要素に作用素を作用させる演算.
  * @param[in] h 作用素モノイドの演算.
  * @param[in] e 要素モノイドの単位元.
  * @param[in] oe 作用素モノイドの単位元.
  * @details 使い方
  *   e.g. Range Add Range Sum
  *   LazySegmentTree<int> segt(
  *            n,
  *            [](int a,int b){ return a+b; },
  *            [](int a,int b,int w){ return a + b*w; },
  *            [](int a,int b){ return a+b; },
  *            0, 0);
  *               // 全て単位元で初期化される.
  */
  LazySegmentTree(
    int n,
    const function<M(M,M)>& f,
    const function<M(M,OM,int)>& g,
    const function<OM(OM,OM)>& h,
    const M& e, const OM& oe
  ) : n(n), f(f), g(g), h(h), e(e), oe(oe) {
    sz = 1;
    while (sz < n) sz <<= 1;
    data.assign(2 * sz, e);
    lazy.assign(2 * sz, oe);
  }

  /**
  * @brief 全体に初期値を入れる. O(n)
  * @param[in] v 要素モノイドのvector. 初期化する.
  * @details 使い方
  *   segt.build(vector<int>(n, INF));
  */
  void build(const vector<M>& v) {
    assert(v.size() <= n);
    for (int i = 0; i < v.size(); ++i) {
      data[i + sz] = v[i];
    }
    for (int i = sz-1; i > 0; --i) {
      data[i] = f(data[2 * i], data[2 * i + 1]);
    }
  }

  /**
  * @brief 指定した区間に作用素x を作用させる. O(log n)
  * @param[in] l, r 区間[l, r) に作用させる.
  * @param[in] x 作用素モノイドの元.
  * @details 使い方
  *   e.g. Range Update
  *   int l, r, x; // 区間[l, r) をx に更新したい.
  *   segt.update(l, r, x);
  */
  void update(int a, int b, const OM& x) {
    update(a, b, x, 1, 0, sz);
  }

  /**
  * @brief 指定した区間に取得クエリを実行する. O(log n)
  * @param[in] l, r 区間[l, r) を取得する.
  * @return 取得した値.
  * @details 使い方
  *   e.g. Range Minimum
  *   int l, r; // 区間[l, r) のminを取得したい.
  *   cout << segt.query(l, r) << endl;
  */
  M query(int a, int b) {
    return query(a, b, 1, 0, sz);
  }

  /**
  * @brief 指定したindexの要素を取得. O(1)
  * @param[in] i 取得したい要素のindex
  * @return 取得した値.
  */
  M operator[](int i) {
    return query(i, i + 1);
  }

  /**
  * @brief vector みたいに出力.
  */
  friend ostream& operator<<(ostream& os, LazySegmentTree& s) {
    os << "[";
    for (int i = 0; i < s.n; ++i) {
      if (i) os << " ";
      os << s[i];
    }
    return os << "]";
  }

private:

  int n, sz;
  vector<M> data;
  vector<OM> lazy;
  const function<M(M,M)> f;
  const function<M(M,OM,int)> g;
  const function<OM(OM,OM)> h;
  const M e;
  const OM oe;

  void propagate(int k, int len) {
    if (lazy[k] == oe) return;
    if (k < sz) {
      lazy[2 * k    ] = h(lazy[2 * k    ], lazy[k]);
      lazy[2 * k + 1] = h(lazy[2 * k + 1], lazy[k]);
    }
    data[k] = g(data[k], lazy[k], len);
    lazy[k] = oe;
  }

  void update(int a, int b, const OM& x, int k, int l, int r) {
    propagate(k, r - l);
    if (r <= a or b <= l) return;
    else if (a <= l and r <= b) {
      lazy[k] = h(lazy[k], x);
      propagate(k, r - l);
    } else {
      update(a, b, x, 2 * k,     l, (l + r) >> 1);
      update(a, b, x, 2 * k + 1, (l + r) >> 1, r);
      data[k] = f(data[2 * k], data[2 * k + 1]);
    }
  }

  M query(int a, int b, int k, int l, int r) {
    propagate(k, r - l);
    if (r <= a or b <= l) return e;
    else if (a <= l and r <= b) return data[k];
    else return f(
        query(a, b, 2 * k,     l, (l + r) >> 1),
        query(a, b, 2 * k + 1, (l + r) >> 1, r));
  }
};

int main() {
  cin.tie(0);
  ios::sync_with_stdio(false);
  cout << fixed << setprecision(10);

  ll n; cin >> n;
  if(n == 2) {
    cout << 4 << endl;
    return 0;
  }
  LazySegmentTree<ll, ll> st(n, [](ll a, ll b){return (a + b) % MOD;},
                              [](ll a, ll b, int w){return (a + b) % MOD * w % MOD;},
                              [](ll a, ll b){return (a + b) % MOD;}, 0, 0);

  st.update(0, 1, 1);

  ll ans = 0;
  REP(i, n-1) {
    int idx = max(1ll, n - i - 2);
    ll tmp = st[i];
    //cout << idx << endl;
    ans += (n - idx + MOD) % MOD * n % MOD * tmp % MOD;
    ans %= MOD;
    ans += (idx - 1) * (n - 1) % MOD * tmp % MOD;
    ans %= MOD;
    st.update(i+1, i+2, tmp);
    if(2 <= idx && i + 3 < n) st.update(i + 3, n, tmp);
    //cout << ans << endl;
  }

  ans += st[n-1] * n % MOD;
  cout << ans % MOD << endl;
}
























Measured memory_bytes: 33152