// Original C++ solution (slower):
#include <iostream>

#include <string>

#include <algorithm>

#include <vector>

#include <cmath>

#include <queue>

#include <deque>

#include <map>

#include <set>

#include <tuple>

#include <stack>

#include <bitset>

#include <utility>

#include<iomanip>

// #include <numeric>

using namespace std;







typedef unsigned long ul;

typedef unsigned long long ull;

typedef long long ll;

typedef vector<ll> vint;

typedef vector<vector<ll>> vvint;

typedef vector<string> vstring;

typedef vector<vector<string>> vvstring;

typedef vector<char> vchar;

typedef vector<vector<char>> vvchar;

typedef vector<long double> vdouble;

typedef vector<vector<long double>> vvdouble;

typedef vector<vector<vector<long double>>> vvvdouble;

typedef pair<ll,ll> pint;

typedef vector<pint> vpint;

typedef vector<bool> vbool;



#define rep(i,n) for(ll i=0;i<n;i++)

#define repf(i,f,n) for(ll i=f;i<n;i++)

#define repr(i,n) for(ll i=n-1;i>=0;i--)

#define mp make_pair

#define mt make_tuple

#define pb push_back

#define pf push_front

#define fi first

#define se second

#define INT_MAX 2147483647

#define LLONG_MAX 9223372036854775807

#define vmax(vec) *max_element(vec.begin(), vec.end())

#define vmin(vec) *min_element(vec.begin(), vec.end())

#define vsort(vec) sort(vec.begin(), vec.end())

#define vsortgr(vec) sort(vec.begin(), vec.end(), greater<ll>())

#define DIVIDER 1000000007





//

// struct Node{

//   vint children;

//   ll index;

//   ll prop;

// };

// struct edge{ll to; ll cost;};







int dy[]={0, 0, 1, -1};

int dx[]={1, -1, 0, 0,};



// ll X,Y,Z,K;

// ll pmax = 30000000000;

// vint A,B,C;









// struct UnionFind {

//     vector<int> par; // par[i]:iの親の番号　(例) par[3] = 2 : 3の親が2

//

//     UnionFind(int N) : par(N) { //最初は全てが根であるとして初期化

//         for(int i = 0; i < N; i++) par[i] = i;

//     }

//

//     int root(int x) { // データxが属する木の根を再帰で得る：root(x) = {xの木の根}

//         if (par[x] == x) return x;

//         return par[x] = root(par[x]);

//     }

//

//     void unite(int x, int y) { // xとyの木を併合

//         int rx = root(x); //xの根をrx

//         int ry = root(y); //yの根をry

//         if (rx == ry) return; //xとyの根が同じ(=同じ木にある)時はそのまま

//         par[rx] = ry; //xとyの根が同じでない(=同じ木にない)時：xの根rxをyの根ryにつける

//     }

//

//     bool same(int x, int y) { // 2つのデータx, yが属する木が同じならtrueを返す

//         int rx = root(x);

//         int ry = root(y);

//         return rx == ry;

//     }

// };

//

// vint divisor(ll n){ // nの約数

//   vint ret;

//   for(ll i=1;i*i<=n;i++){

//     if(n%i == 0){

//       ret.pb(i);

//       if(i*i!=n) ret.pb(n/i);

//     }

//   }

//   vsort(ret);

//   return ret;

// }

//

ll my_pow(ll x, ll y){

  ll rtn = 1;

  rep(i,y) rtn*=x;

  return rtn;

}



ll get_digits_num(ll n){

  ll ret=0;

  while(n){

    n/=10;

    ret++;

  }

  return ret;

}



// ll gcd(ll m, ll n){

//   if(m<n) swap(m,n);

//   if(n==0) return m;

//   return gcd(n, m%n);

// }







ll get_digits_in(ll n,ll m, ll max_digits){

  rep(i,max_digits-m){

    n/=10;

  }

  return n%10;

}



int main() {

  cout<<fixed<<setprecision(10);



  ll n;cin>>n;

  vint x(n),y(n),h(n);

  rep(i,n) cin>>x[i]>>y[i]>>h[i];

  vector<tuple<ll,ll,ll>> arr(n);

  rep(i,n) arr[i] = mt(h[i],x[i],y[i]);

  vsort(arr);

  ll nonezero = lower_bound(arr.begin(), arr.end(), mt(1,-1,-1)) - arr.begin();

  ll cx,cy,ch;



  rep(i,101){

    rep(j,101){

      bool flag = true;

      ll a,b,c;

      tie(a,b,c) = arr[nonezero];

      ll H = a + abs(b-i) + abs(c-j);

      for(ll k=nonezero;k<n;k++){

        tie(a,b,c) = arr[k];

        ll nextH = a + abs(b-i) + abs(c-j);

        flag = flag && (H==nextH);

      }

      if(!flag) continue;

      for(ll k=0;k<nonezero;k++){

        tie(a,b,c) = arr[k];

        flag = flag && (max(H-abs(b-i)-abs(c-j), 0ll) == a);

      }

      if(flag){

        cx = i;

        cy = j;

        ch = H;

      }

    }

  }



  cout << cx << " " << cy << " " << ch<<endl;



}



































//


// Optimized solution to the same problem (faster):
#include <bits/stdc++.h>

using namespace std;







typedef unsigned long ul;

typedef unsigned long long ull;

typedef long long ll;

typedef vector<ll> vint;

typedef vector<vector<ll>> vvint;

typedef vector<vector<vector<ll>>> vvvint;

typedef vector<string> vstring;

typedef vector<vector<string>> vvstring;

typedef vector<char> vchar;

typedef vector<vector<char>> vvchar;

typedef vector<long double> vdouble;

typedef vector<vector<long double>> vvdouble;

typedef vector<vector<vector<long double>>> vvvdouble;

typedef pair<ll,ll> pint;

typedef vector<pint> vpint;

typedef vector<bool> vbool;



#define rep(i,n) for(ll i=0;i<n;i++)

#define repf(i,f,n) for(ll i=f;i<n;i++)

#define repr(i,n) for(ll i=n-1;i>=0;i--)

#define mp make_pair

#define mt make_tuple

#define pb push_back

#define pf push_front

#define fi first

#define se second

#define ALL(obj) (obj).begin(), (obj).end()

// #define LLONG_MAX 9223372036854775806

#define vmax(vec) *max_element(vec.begin(), vec.end())

#define vmin(vec) *min_element(vec.begin(), vec.end())

#define vsort(vec) sort(vec.begin(), vec.end())

#define vsortgr(vec) sort(vec.begin(), vec.end(), greater<ll>())

#define MOD 1000000007

// #define MOD 998244353



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

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



//

// struct Node{

//   vint children;

//   ll index;

//   ll prop;

// };

// struct edge{ll to; ll cost;};







int dy[]={0, 0, 1, -1};

int dx[]={1, -1, 0, 0,};



// ll X,Y,Z,K;

// ll pmax = 30000000000;

// vint A,B,C;



// void comb(vector<vector <long long int> > &v){

//   for(int i = 0;i <v.size(); i++){

//     v[i][0]=1;

//     v[i][i]=1;

//   }

//   for(int k = 1;k <v.size();k++){

//     for(int j = 1;j<k;j++){

//       v[k][j]=((v[k-1][j-1]+v[k-1][j])%MOD);

//     }

//   }

// }

ll gcd(ll a, ll b)

{

    if (a < b) {

        a ^= b;

        b ^= a;

        a ^= b;

    }



    return b ? gcd(b, a % b) : a;

}



ll lcm(int a, ll b)

{

    return a * b / gcd(a, b);

}









// 繰り返し二乗法

ll power(ll a, ll b){

	ll res=1;

	while(b>0){

		if(b&1) res=res*a%MOD;

		a=a*a%MOD;

		b>>=1;

	}

	return res;

}



const int MAX=510000;



ll fact[MAX], fact_inv[MAX];



void init_fact(ll n){

  fact[0]=1;

  // 階乗の計算

  rep(i, n) fact[i+1]=fact[i]*(i+1)%MOD;

  fact_inv[n]=power(fact[n], MOD-2);

  // 逆元の計算

  for(ll i=n-1;i>=0;i--) fact_inv[i]=fact_inv[i+1]*(i+1)%MOD;

}



ll comb(ll n, ll r){

	return (fact[n]*fact_inv[r])%MOD*fact_inv[n-r]%MOD;

}



ll perm(ll n, ll r){

  return (fact[n]*fact_inv[n-r])%MOD;

}





struct UnionFind {

    vector<ll> par; // par[i]:iの親の番号　(例) par[3] = 2 : 3の親が2

    vector<ll> num;

    // vector<ll> dist; // rootまでの距離



    UnionFind(ll N) : par(N), num(N){ //最初は全てが根であるとして初期化

      for(ll i = 0; i < N; i++) par[i] = i;

      for(ll i = 0; i < N; i++) num[i] = 1;

      // for(ll i = 0; i < N; i++) dist[i] = 0;



    }



    ll root(ll x) { // データxが属する木の根を再帰で得る：root(x) = {xの木の根}

        if (par[x] == x) return x;

        return par[x] = root(par[x]);

    }



    void unite(ll x, ll y) { // xとyの木を併合

        ll rx = root(x); //xの根をrx

        ll ry = root(y); //yの根をry

        if (rx == ry) return; //xとyの根が同じ(=同じ木にある)時はそのまま

        par[rx] = ry; //xとyの根が同じでない(=同じ木にない)時：xの根rxをyの根ryにつける

        num[ry] = num[rx] + num[ry];

    }







    bool same(ll x, ll y) { // 2つのデータx, yが属する木が同じならtrueを返す

        ll rx = root(x);

        ll ry = root(y);

        return rx == ry;

    }



    ll size(ll x){

      return num[root(x)];

    }





};



vint divisor(ll n){ // nの約数

  vint ret;

  for(ll i=1;i*i<=n;i++){

    if(n%i == 0){

      ret.pb(i);

      if(i*i!=n) ret.pb(n/i);

    }

  }

  vsort(ret);

  return ret;

}



ll my_pow(ll x,ll y){

  ll rtn=1;

  rep(i,y) rtn*=x;

  return rtn;

}



ll get_digit_in(ll n, ll i){ // i桁目の数字を得る。

  for(ll j=0;j<i-1;j++){

    n/=10;

  }

  return n%10;

}



ll get_digit(ll n){

  ll rtn=0;

  while(n>0){

    n/=10;

    rtn++;

  }

  return rtn;

}



map<ll,ll> prime_factor(ll n){

  map<ll,ll> rtn;

  for(ll i=2;i*i<=n;i++){

    while(n%i == 0){

      rtn[i]++;

      n /= i;

    }

  }

  if(n!=1) rtn[n] = 1;

  return rtn;

}



// nPnの順列に対して処理を実行する

void foreach_permutation(ll n, function<void(ll *)> f) {

  ll indexes[n];

  for (ll i = 0; i < n; i++) indexes[i] = i;

  do {

    f(indexes);

  } while (std::next_permutation(indexes, indexes + n));

}



void recursive_comb(ll *indexes, ll s, ll rest, function<void(ll *)> f) {

  if (rest == 0) {

    f(indexes);

  } else {

    if (s < 0) return;

    recursive_comb(indexes, s - 1, rest, f);

    indexes[rest - 1] = s;

    recursive_comb(indexes, s - 1, rest - 1, f);

  }

}



// nCkの組み合わせに対して処理を実行する

void foreach_comb(ll n, ll k, function<void(ll *)> f) {

  ll indexes[k];

  recursive_comb(indexes, n - 1, k, f);

}



// nPkの順列に対して処理を実行する

void foreach_permutation(ll n, ll k, function<void(ll *)> f) {

  foreach_comb(n, k, [&](ll *c_indexes) {

    foreach_permutation(k, [&](ll *p_indexes) {

      ll indexes[k];

      for (ll i = 0; i < k; i++) {

        indexes[i] = c_indexes[p_indexes[i]];

      }

      f(indexes);

    });

  });

}



// ll arr[55556];

// vint primes;

//

// //エラトステネスの篩

// void Eratosthenes(){

//   ll N = 55556;

// 	for(ll i = 0; i < N; i++){

// 		arr[i] = 1;

// 	}

// 	for(ll i = 2; i < sqrt(N); i++){

// 		if(arr[i]){

// 			for(ll j = 0; i * (j + 2) < N; j++){

// 				arr[i *(j + 2)] = 0;

// 			}

// 		}

// 	}

//

// 	for(ll i = 2; i < N; i++){

// 		if(arr[i] && i%5==1){

//       primes.pb(i);

// 			//cout << i << endl;

// 		}

// 	}

// }





void print(vint &V){

  for(auto e:V)cout<<e<<" ";

  cout<<endl;

}













int main() {

  cout<<fixed<<setprecision(12);



  ll N;cin>>N;

  vint x(N),y(N),h(N);

  rep(i,N)cin>>x[i]>>y[i]>>h[i];





  ll nonezero = 0;

  rep(i,N){

    if(h[i]>0){

      nonezero = i;

      break;

    }

  }

  // assert(nonezero<N);



  ll cx=-1, cy=-1;



  bool f;

  ll H;



  for(ll i=0;i<=100;i++){

    for(ll j=0;j<=100;j++){

      f = true;



      H = h[nonezero] + abs(x[nonezero]-i) + abs(y[nonezero]-j);

      rep(k,N){

        // cout<<h[k] + abs(x[k]-i) + abs(y[k]-j)<<endl;

        // if(h[k] == 0)continue;

        // if(H == 0){ H = h[k] + abs(x[k]-i) + abs(y[k]-j); continue;}

        if(h[k] != max(H- abs(x[k]-i) - abs(y[k]-j), 0ll)){

          f = false;

          break;

        }

      }

      if(f && H>0){

        cx = i; cy = j;

        break;

      }

      f = false;



    }

    if(f) break;



  }



  // assert(cx>=0 && cy>=0);



  cout<<cx<<" "<<cy<<" "<<H<<endl;





}









//


// Fastest known solution for this problem: