#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <climits>
#include <cfloat>
#include <ctime>
#include <cassert>
#include <map>
#include <utility>
#include <set>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <sstream>
#include <complex>
#include <stack>
#include <queue>
#include <numeric>
#include <list>
#include <iomanip>
  
using namespace std;
  
#if __GNUC__
#include <tr1/unordered_map>
#include <tr1/unordered_set>
using namespace tr1;
#else
#include <unordered_map>
#include <unordered_set>
#endif
  
  
  
#ifdef _MSC_VER
#define __typeof__ decltype
template <class T> int popcount(T n) { return n ? 1 + popcount(n & (n - 1)) : 0; }
#endif
#ifdef __GNUC__
template <class T> int popcount(T n);
template <> int popcount(unsigned int n) { return __builtin_popcount(n); }
template <> int popcount(int n) { return __builtin_popcount(n); }
template <> int popcount(unsigned long long n) { return __builtin_popcountll(n); }
template <> int popcount(long long n) { return __builtin_popcountll(n); }
#endif
  
#define rep (i, n) for (int i = 0; i < (int)n; ++i)
#define foreach(it, c) for (__typeof__((c).begin()) it=(c).begin(); it != (c).end(); ++it)
#define rforeach(it, c) for (__typeof__((c).rbegin()) it=(c).rbegin(); it != (c).rend(); ++it)
#define all(c) (c).begin(), (c).end()
#define rall(c) (c).rbegin(), (c).rend()
#define CL(arr, val) memset(arr, val, sizeof(arr))
#define MEMCPY(dest, src) memcpy(dest, src, sizeof(src))
  
  
template <class T> void max_swap(T& a, const T& b) { a = max(a, b); }
template <class T> void min_swap(T& a, const T& b) { a = min(a, b); }
  
typedef long long ll;
typedef pair<int, int> pint;
  
template <class T> string to_s(const T& a) { ostringstream os; os << a; return os.str(); }
  
const double EPS = 1e-8;
const double PI = acos(-1.0);
const int dx[] = { 0, 1, 0, -1 };
const int dy[] = { 1, 0, -1, 0 };
bool valid_pos(int x, int y, int w, int h) { return 0 <= x && x < w && 0 <= y && y < h; }
  
template <class T> void print(T a, int n, int br = 1, const string& deli = ", ") { cout << "{ "; for (int i = 0; i < n; ++i) { cout << a[i]; if (i + 1 != n) cout << deli; } cout << " }"; while (br--) cout << endl; }
template <class T> void print(const vector<T>& v, int br = 1, const string& deli = ", ") { print(v, v.size(), br, deli); }
template <class T> void print2d(T a, int w, int h, int width = -1, int br = 1) { for (int i = 0; i < h; ++i) { for (int j = 0; j < w; ++j) {    if (width != -1) cout.width(width); cout << a[i][j] << ' '; } cout << endl; } while (br--) cout << endl; }
  
template <class T> void input(int n, T& a) { for (int i = 0; i < n; ++i) cin >> a[i]; }
template <class T, class U> void input(int n, T& a, U& b) { for (int i = 0; i < n; ++i) cin >> a[i] >> b[i]; }
template <class T> vector<T> input(int n) { vector<T> res(n); for (int i = 0; i < n; ++i) cin >> res[i]; return res; }
 
 
 
 const double EPS_FOR_LIB = 1e-9;
typedef double geo_type;
typedef complex<geo_type> Point;
typedef vector<Point> Poly;
namespace std
{
	bool operator<(const Point& a, const Point& b)
	{
		return a.real() != b.real() ? a.real() < b.real() : a.imag() < b.imag();
	}
	std::istream& operator>>(std::istream& is, Point& p)
	{
		geo_type a, b; is >> a >> b;
		p = Point(a, b);
		return is;
	}
	std::ostream& operator<<(std::ostream& os, Point& p)
	{
		return os << "(" << p.real() << ", " << p.imag() << ")";
	}
};
struct Line
{
	Point a, b;
	Line(Point a, Point b) : a(a), b(b) { }
	Line() { }
};

geo_type dot(const Point& a, const Point& b)
{
	return a.real() * b.real() + a.imag() * b.imag();
}
geo_type cross(const Point& a, const Point& b)
{
	return a.real() * b.imag() - a.imag() * b.real();
}
bool is_orthogonal(const Line& line1, const Line& line2)
{
	return abs(dot(line1.b - line1.a, line2.b - line2.a)) < 1e-20;	//EPS_FOR_LIB;
}
bool is_parallel(const Line& line1, const Line& line2)
{
	return abs(cross(line1.b - line1.a, line2.b - line2.a)) < 1e-20;	//EPS_FOR_LIB;
}

bool is_on_line(const Line& line, const Point& p)
{
	return abs(cross(line.b - line.a, p - line.a)) < 1e-20;
}
bool is_on_seg(const Line& seg, const Point& p)
{
	return abs(seg.a - p) + abs(p - seg.b) < abs(seg.a - seg.b) + EPS_FOR_LIB;
}

bool intersect_LL(const Line& line1, const Line& line2)
{
	return !is_parallel(line1, line2);
}
bool intersect_LS(const Line& line, const Line& seg)
{
	return cross(line.b - line.a, seg.a - line.a)
		* cross(line.b - line.a, seg.b - line.a) < EPS_FOR_LIB;
}
bool intersect_SS(const Line& seg1, const Line& seg2)
{
	return cross(seg1.b - seg1.a, seg2.a - seg1.a) * cross(seg1.b - seg1.a, seg2.b - seg1.a) < -EPS_FOR_LIB
		&& cross(seg2.b - seg2.a, seg1.a - seg2.a) * cross(seg2.b - seg2.a, seg1.b - seg2.a) < -EPS_FOR_LIB
		|| is_on_seg(seg1, seg2.a)
		|| is_on_seg(seg1, seg2.b)
		|| is_on_seg(seg2, seg1.a)
		|| is_on_seg(seg2, seg1.b);
}

Point cross_point_LL(const Line& line1, const Line& line2)
{
#ifdef DEBUG
	if (is_parallel(line1, line2))
	{
		cerr << "cross_point_LL: 前提条件満たしてない" << endl;
		exit(1);
	}
#endif
	Point a = line1.b - line1.a, b = line2.b - line2.a;
	double p = cross(b, line2.a - line1.a);
	double q = cross(b, a);
	return line1.a + p / q * a;
}

// not verified
double area(const Poly& p)
{
	double s = 0;
	for (int i = 0; i < p.size(); ++i)
		s += cross(p[i], p[(i + 1) % p.size()]);
	return abs(s / 2);
}

// 三角形のみverify
enum { OUT, IN, ON };
int contain(const Poly& poly, const Point& p)
{
	bool in = false;
	for (int i = 0; i < poly.size(); ++i)
	{
		Point a = poly[i] - p, b = poly[(i + 1) % poly.size()] - p;
		if (a.imag() > b.imag())
			swap(a, b);
		if ((a.imag() <= 0 && 0 < b.imag()) && cross(a, b) < 0)
			in = !in;
		if (is_on_seg(Line(poly[i], poly[(i + 1) % poly.size()]), p))
			return ON;
	}
	return in ? IN : OUT;
}
//

int main()
{
	Poly p(4);
	while (input(4, p), cin)
	{
		Line a(p[0], p[1]), b(p[2], p[3]);
		puts(is_orthogonal(a, b) ? "YES" : "NO");
	}
}

Measured memory_bytes: 0