#include <bits/stdc++.h>

#define debug(x) cout <<"DEBUG"<<" "<< #x << ":" << x << '\n'
#define rep(i,n) for(int_fast32_t i = 0; i < ((int_fast32_t)(n)); i++)   // 0-indexed昇順
#define rep1(i,n) for(int_fast32_t i = 1; i <= ((int_fast32_t)(n)); i++) // 1-indexed昇順
#define rrep(i,n) for(int_fast32_t i = ((int_fast32_t)(n)-1); i >= 0; i--)  // 0-indexed降順
#define rrep1(i,n) for(int_fast32_t i = ((int_fast32_t)(n)); i >= 1; i--)   // 1-indexed降順
#define all(x) (x).begin(),(x).end()

using namespace std;

template<typename T>
using vec = vector<T>;
using i32 = int_fast32_t;
using i64 = int_fast64_t;
using u32 = uint_fast32_t;
using u64 = uint_fast64_t;
using ll = long long;
using ld = long double;
using vi = vec<int_fast32_t>;
using vl = vec<int_fast64_t>;
using vld = vec<ld>;
using vii = vec<vi>;  // 2次元配列
using PII = pair<int_fast32_t, int_fast32_t>;

template<class T>
using maxheap = std::priority_queue<T>;
template<class T>
using minheap = std::priority_queue<T, std::vector<T>, std::greater<T>>;

template<class T,class U> inline bool chmax(T &a, const U &b){if(a<b){a=b;return 1;}return 0;}
template<class T,class U> inline bool chmin(T &a, const U &b){if(a>b){a=b;return 1;}return 0;}

const ld Pi = std::acos(-1.0L);
constexpr ll infll = (1LL<<62)-1;
constexpr int inf = (1<<30)-1;
const int mod = 1000000007;
// const int mod = 998244353;

/*UnionFind--------------------------*/
class UnionFind {
using i32 = int_fast32_t;
private:
    std::vector<i32> parent;
    std::vector<i32> siz;

public:
    UnionFind(i32 sz): parent(sz), siz(sz, 1) {
        for(i32 i = 0; i < sz; ++i) parent[i] = i;
    }
    void init(i32 sz) {
        for(i32 i = 0; i < sz; ++i) parent[i] = i;
    }
    /*Member Func------*/
    /*return root id*/
    i32 root(i32 x) {
        while(parent[x] != x) {
            x = parent[x] = parent[parent[x]];
        }
        return x;
    }
    /*merge two groups*/
    bool merge(i32 x, i32 y) {
        x = root(x);
        y = root(y);
        if(x == y) return false;
        //Merge Tech
        if (siz[x] < siz[y]) std::swap(x,y);
        siz[x] += siz[y];
        parent[y] = x;
        return true;
    }
    /*同じ集合にあるか*/
    bool issame(i32 x, i32 y) {
        return (root(x) == root(y));
    }
    /*xの属する集合の大きさ*/
    i32 size(i32 x) {
        return siz[root(x)];
    }
};

/*FUNCs=================================================*/


/*MAIN==================================================*/
signed main(){
    int START_TIME = clock();
    cin.tie(nullptr);
    ios::sync_with_stdio(false);   //cin cout 高速化
//    cout << fixed << setprecision(15);

    i32 n; cin >> n;

    /**
     * 直線状に並ぶボールの数がサ台になるようにpqを決めると良い。
     * その直線上で等間隔に並んだものは合わせてコスト1で回収でき、その他はそれぞれコスト１かかる
     * 各座標の差をmapで管理し、p,qを定める。その後x,yが最大のものから見て順に処理
     */

    map<PII, i32> pq;
    vec<PII> xy(n); rep(i,n) {
        cin >> xy[i].first >> xy[i].second;
    }

    for(i32 i = 0; i < n; ++i) {
        for(i32 j = 0; j < n; ++j) if(i != j) {
            i32 p = xy[i].first - xy[j].first, q = xy[i].second - xy[j].second;
            ++pq[{p,q}];
        }
    }

    i32 tmp = -1, p, q;
    for(auto pr : pq) {
        if(chmax(tmp, pr.second)) {
            p = pr.first.first, q = pr.first.second;
        }
    }
//    debug(p), debug(q);

    UnionFind uf(n);
    for(i32 i = 0; i < n; ++i) {
        for(i32 j = 0; j < n; ++j) if(i != j) {
            if(xy[i].first - xy[j].first == p && xy[i].second - xy[j].second == q) {
                uf.merge(i,j);
            }
        }
    }
    i32 cnt = 0;
    rep(i,n) {
        if(uf.root(i) == i) ++cnt;
    }

    cout << cnt << '\n';

    // printf("ExecutionTime: %d /ms\n",1000*(int)((clock()-START_TIME)/CLOCKS_PER_SEC));
}


Measured memory_bytes: 384