#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
#define mod 1000000007
#define MX 100001
#define pb push_back
#define mp make_pair
#define test ll cases; cin>>cases; while(cases--)   //test cases
#define error(x) fixed<<setprecision(x) //cout<<error(5)<<someDouble    -> example - 3.14159
#define vll vector<ll>
#define inf 1e18
#define rep(i, begin, end, upd)  for(ll i = begin; ((begin < end && upd > 0 && i < end) || (begin > end && upd < 0 && i >= end)); i+=upd)
#define mk(name, n, type)  type* name = new type[n]
#define inp(name, n, type) mk(name, n, type); rep(i, 0, n) cin>>name[i];
#define fill(name, val) memset(name, val, sizeof(name))
#define boost ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL)
vll primes;
ll segOp(ll a, ll b);
class TreeNode{
    public:
    ll val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(ll v){
        val = v;
        left = right = NULL;
    }
};
class STree{    //For range queries
    ll* tree;
    ll* lazy;
    ll queryBase;
    public:
    void build(ll* arr, ll index, ll start, ll end){
        if(start == end){
            tree[index] = arr[start];
            return;
        }
        ll mid = (start + end) >> 1;
        build(arr, 2*index, start, mid);
        build(arr, 2*index+1, mid+1, end);
        tree[index] = segOp(tree[2*index], tree[2*index+1]);
    }
    void update(ll index, ll start, ll end, ll i, ll val){
        if(start > end || start > i || end < i) return;
        if(start == end){
            tree[index] = val;
            return;
        }
        ll mid = (start + end)>>1;
        update(2*index, start, mid, i, val);
        update(2*index+1, mid+1, end, i, val);
        tree[index] = segOp(tree[2*index], tree[2*index+1]);
    }
    ll query(ll index, ll start, ll end, ll left, ll right){
        if(start > right || end < left || start > end)  return queryBase;
        if(start >= left && end <= right)   return tree[index];
        ll mid = (start + end)>>1;
        ll op1 = query(2*index, start, mid, left, right);
        ll op2 = query(2*index+1, mid+1, end, left, right);
        return segOp(op1, op2);
    }
    void passLaziness(ll start, ll end, ll index){
        if(start == end)    return;
        lazy[2*index] += lazy[index];
        lazy[2*index+1] += lazy[index];
    }
    void lazyUpdate(ll index, ll start, ll end, ll left, ll right, ll val);
    ll lazyQuery(ll index, ll start, ll end, ll left, ll right);
    STree(ll* arr, ll n, ll qb, bool lazyTree = false){
        tree = new ll[4*n+1];
        if(lazyTree)    lazy = new ll[4*n+1]();
        queryBase = qb;
        build(arr, 1, 0, n-1);
    }
};
class BIT{      //For prefix range queries
    ll* tree;
    ll maxn;
    ll (*func)(ll, ll);
    public:
    void update(ll index, ll val){
        while(index <= maxn){
            tree[index] = (*func)(tree[index], val);
            index += (index & (-index));
        }
    }
    ll query(ll index){
        ll ans = 0;
        while(index > 0){
            ans = (*func)(ans, tree[index]);
            index -= (index & (-index));
        }
        return ans;
    }
    BIT(ll* arr, ll n, ll (*f)(ll, ll)){
        tree = new ll[n+1]();
        maxn = n;
        func = f;
        rep(i, 1, n+1, 1)   update(i, arr[i-1]);
    }
};
class Graph{
    ll N;
    bool weighted;
    unordered_map<ll, unordered_map<ll, ll>> g; //Assume all graphs are weighted. Unweighted => every edge weight = 1
    public:
    Graph(ll n, bool w = false){
        N = n;
        weighted = w;
    }
    void addEdge(ll u, ll v, ll w = 1){
        g[u][v] = w;
    }
    void printEdges(){
        for(auto u : g){
            cout<<u.first<<"-> ";
            for(auto v : u.second){
                cout<<v.first;
                if(weighted) cout<<"("<<v.second<<")";
                cout<<' ';
            }
            cout<<endl;
        }
    }
};
ll gcd(ll a, ll b){
    if(a == 0)  return b;
    return gcd(b%a, a);
}
ll power(ll x, ll n, ll m = mod){
    if(n == 0)  return 1;
    ll sa = power(x, n/2)%m;
    sa = (sa*sa)%m;
    return (sa*(x&1?x:1))%m;
}
ll modInv(ll x, ll m = mod){
    if(gcd(x, m) != 1)  return -1;
    return power(x, m-2, m);
}
void primeSieve(ll n = MX){
    if(primes.size())   return;
    bool nums[n];   fill(nums, true);
    primes.pb(2);
    rep(i, 4, n, 2)   nums[i] = false;
    rep(i, 3, n, 2){
        if(nums[i]){
            primes.pb(i);
            rep(j, i*i, n, i) nums[j] = false;
        }
    }
}
void getPrimes(ll n = MX){
    ll limit = floor(sqrt(n))+1;
    if(!primes.size())  primeSieve(limit);
    ll low = limit, high = 2*limit;
    bool mark[limit+1];
    while(low < n){
        high = min(n, high);
        fill(mark, true);
        rep(i, 0, primes.size(), 1){
            ll lowLim = floor(low/primes[i])*primes[i];
            if(lowLim < low)    lowLim += primes[i];
            rep(j, lowLim, high, primes[i])  mark[j-low] = false;
        }
        rep(i, low, high, 1)    if(mark[i-low]) primes.pb(i);
        low += limit;
        high += limit;
    }
}
ll nCr(ll n, ll r, ll m = mod){
    if(r == 0)  return 1;
    ll fact[n+1];   fact[0] = 1;
    rep(i, 1, n+1, 1)   fact[i] = (fact[i-1]*i)%m;
    rep(i, 1, n+1, 1)   cout<<fact[i]<<' ';
    return (((fact[n] * modInv(fact[r], m)) % m) * modInv(fact[n-r], m)) % m;
    //return (fact[n] * (modInv(fact[r], m) % m) * (modInv(fact[n-r], m) % m)) % m;
}
vll factorize(ll n){
    vll res;
    ll rootn = sqrt(n);
    rep(i, 1, rootn+1, 1){
        if(n % i == 0){
            res.pb(i);
            if(n/i != i)    res.pb(n/i);
        }
    }
    return res;
}
void STree::lazyUpdate(ll index, ll start, ll end, ll left, ll right, ll val){
    //Case 1: Pending update at index
    if(lazy[index]){
        tree[index] = 0;    //Update tree index as per requirement
        passLaziness(start, end, index);
        lazy[index] = 0;    //Remove laziness from node
    }
    //Case 2: Out of range
    if(start > end || start > right || end < left)  return;
    //Case 3: Segment fully in range
    if(start >= left && end <= right){
        tree[index] = 0;    //Update tree index as per requirement
        passLaziness(start, end, index);
        return;
    }
    //Case 4: Segment partially in range
    ll mid = (start + end) >> 1;
    lazyUpdate(2*index, start, mid, left, right, val);
    lazyUpdate(2*index+1, mid+1, end, left, right, val);
    tree[index] = segOp(tree[2*index], tree[2*index+1]);
}
ll STree::lazyQuery(ll index, ll start, ll end, ll left, ll right){
    //Case 1: Out of range
    if(start > end || start > right || end < left)  return queryBase;
    //Case 2: Pending updates
    if(lazy[index]){
        tree[index] = 0;    //Update tree index as per requirement
        passLaziness(start, end, index);
        lazy[index] = 0;
    }
    //Case 3: Segment fully in range
    if(start >= left && end <= right)   return tree[index];
    //Case 4: Segment partially in range
    ll mid = (start + end) >> 1;
    ll leftQuery = lazyQuery(2*index, start, mid, left, right);
    ll rightQuery = lazyQuery(2*index+1, mid+1, end, left, right);
    return segOp(leftQuery, rightQuery);
}
/*  =======TL;DR=======
    Author : zenolus
    TreeNode (binary tree):
        TreeNode(v)   => create new node with val v
    STree (segment tree):
        STree(array, size, queryBaseCase, lazyTree = false) => build segment tree
        segOp(op1, op2) => perform segment operation to update tree[index]
        update(1, 0, size-1, arrayIndex, value)   => updates tree, modify original array separately
        query(1, 0, size-1, range_left, range_right)  => returns segment operation for range
        lazyUpdate(1, 0, size-1, range_left, range_right, val) => Lazy propagation. Modify tree[index] assignment.
        lazyQuery(1, 0, size-1, range_left, range_right)    => Lazy query on range. Modify tree[index] assignment.
    BIT (binary indexed tree / Fenwick tree):
        NOTE: segment operation needs to be associative and has to have an inverse
        BIT(array, size, ll (*segment operation)(ll, ll))   => build BIT
        update(index, val)  => point update
        query(index)    => prefix range query
    Graph (Adjacency List representation):
        Graph(nodes, weighted = false)  => create a graph with n nodes, specify if weighted
        addEdge(u, v, weight = 1)   => add an edge in graph, specify weight if necessary
        printEdges() => display the adjacency list
    rep(i, begin, end, update) => for(type i : (start -> end))
    gcd(a, b)   => returns gcd of a and b
    power(x, n, m = mod) => return x raised to power n in O(log n) time, if required, specify modulo
    modInverse(x, m = mod)  => returns modular multiplicative inverse of x with m
    getPrimes(n = MX)   => stores all the primes till n in global vector. Uses segmented sieve. Simple sieve in primeSieve(n)
    factorize(n)    => returns a vector of factors of n
    nCr(n, r, m = mod)  => returns nCr value. ATTENTION: Needs fixing
*/
ll segOp(ll a, ll b);
class STreeCP{
    //Reference : https://codeforces.com/blog/entry/18051
    public:
    ll n;
    ll* tree;
    ll queryBase;
    void getArray(){
        rep(i, 0, n, 1) cin>>tree[n+i];
    }
    void build(){   // build the tree
        rep(i, n-1, 0, -1)  tree[i] = segOp(tree[i<<1], tree[i<<1|1]);
    }
    void update(ll index, ll value){    //update point value
        for(tree[index += n] = value; index > 1; index >>= 1)   tree[index>>1] = segOp(tree[index], tree[index^1]);
    }
    void update(ll l, ll r, ll value){   //update on interval [l,r)
        for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
            if (l&1) tree[l++] += value;// segOp(tree[l], value);
            if (r&1) tree[--r] += value;//segOp(tree[r], value);//tree[r-1] = segOp(value, tree[r---1]); //tree[--r] = segOp(value, tree[r]);
        }
    }
    ll query(ll l, ll r){   //query on interval [l, r)
        ll resl = 0, resr = 0;  //Modify return type of res and segOp as per requirement
        for(l += n, r += n; l < r; l >>= 1, r >>= 1){
            if (l&1) resl = segOp(resl, tree[l++]);
            if (r&1) resr = segOp(resr, tree[--r]);
        }
        return segOp(resl, resr);
    }
    ll query(ll p){ //single point query
        ll res = 0;
        for (p += n; p > 0; p >>= 1) res = segOp(res, tree[p]);
        return res;
    }
    void display(){
        rep(i, 0, 2*n, 1)   cout<<tree[i]<<' ';
    }
    STreeCP(ll num, ll queryBase){
        n = num;
        tree = new ll[2*n]; tree[0] = 0;
        getArray();
        build();
    }
};
ll segOp(ll a, ll b){
    return a+b;
}
int main(){
    boost;
    ll n;   cin>>n;
    ll sum = 0;
    for(ll i = 1; i <= n; i++){
        for(ll j = 1; j <= n; j++){
            for(ll k = 1; k <= n; k++){
                sum += gcd(gcd(i, j), k);
            }
        }
    }
    cout<<sum;
}

Measured memory_bytes: 3680