All Euler problems
Project Euler

Triangle Containment

Three distinct points are plotted at random on a Cartesian plane, for which -1000 <= x, y <= 1000, such that a triangle is formed. Given a file containing the coordinates of one thousand triangles,...

Source sync May 21, 2026
Problem #0102
Level Level 03
Solved By 24,602
Languages C++, Python
Answer 228
Length 389 words
geometryprobability

Problem Statement

This archive keeps the full statement, math, and original media on the page.

Three distinct points are plotted at random on a Cartesian plane, for which \(-1000 \le x, y \le 1000\), such that a triangle is formed.

Consider the following two triangles: \begin {gather*} A(-340,495), B(-153,-910), C(835,-947)\\ X(-175,41), Y(-421,-714), Z(574,-645) \end {gather*} It can be verified that triangle \(ABC\) contains the origin, whereas triangle \(XYZ\) does not.

Using triangles.txt (right click and ’Save Link/Target As...’), a 27K text file containing the co-ordinates of one thousand "random" triangles, find the number of triangles for which the interior contains the origin.

NOTE: The first two examples in the file represent the triangles in the example given above.

Problem 102: Triangle Containment

Mathematical Development

Definition. For points U=(ux,uy)U = (u_x, u_y), V=(vx,vy)V = (v_x, v_y), W=(wx,wy)W = (w_x, w_y) in R2\mathbb{R}^2, define the signed area function:

σ(U,V,W)=12[(vx−ux)(wy−uy)−(wx−ux)(vy−uy)].\sigma(U, V, W) = \frac{1}{2}\bigl[(v_x - u_x)(w_y - u_y) - (w_x - u_x)(v_y - u_y)\bigr].

Equivalently, σ(U,V,W)=12det⁡ ⁣(vx−uxwx−uxvy−uywy−uy)\sigma(U, V, W) = \frac{1}{2}\det\!\begin{pmatrix} v_x - u_x & w_x - u_x \\ v_y - u_y & w_y - u_y \end{pmatrix}.

Theorem 1 (Point-in-Triangle via Signed Areas). Let A,B,CA, B, C be the vertices of a non-degenerate triangle and let PP be a point in R2\mathbb{R}^2. Then PP lies in the interior of △ABC\triangle ABC if and only if σ(A,B,P)\sigma(A, B, P), σ(B,C,P)\sigma(B, C, P), and σ(C,A,P)\sigma(C, A, P) all have the same nonzero sign.

Proof. The directed edge AB→\overrightarrow{AB} partitions R2∖ℓAB\mathbb{R}^2 \setminus \ell_{AB} into two open half-planes, where ℓAB\ell_{AB} is the line through AA and BB. The sign of σ(A,B,P)\sigma(A, B, P) determines in which half-plane PP lies: positive if PP is to the left of AB→\overrightarrow{AB} (counterclockwise side), negative if to the right.

If △ABC\triangle ABC has counterclockwise orientation (σ(A,B,C)>0\sigma(A, B, C) > 0), then the interior equals the intersection of the three left half-planes defined by AB→\overrightarrow{AB}, BC→\overrightarrow{BC}, CA→\overrightarrow{CA}. Hence PP is interior if and only if σ(A,B,P)>0\sigma(A, B, P) > 0, σ(B,C,P)>0\sigma(B, C, P) > 0, and σ(C,A,P)>0\sigma(C, A, P) > 0.

If △ABC\triangle ABC has clockwise orientation (σ(A,B,C)<0\sigma(A, B, C) < 0), the interior is the intersection of the three right half-planes, and all three signed areas are negative. In both cases, PP is interior if and only if all three signed areas share the same nonzero sign. ■\blacksquare

Lemma 1 (Simplification for P=OP = O). When P=(0,0)P = (0, 0) is the origin, the half-plane tests reduce to cross products of position vectors:

2 σ(A,B,O)=axby−bxay=(OA⃗×OB⃗)z.2\,\sigma(A, B, O) = a_x b_y - b_x a_y = (\vec{OA} \times \vec{OB})_z.

Proof. Substituting P=(0,0)P = (0, 0) into the signed area formula:

2 σ(A,B,O)=(bx−ax)(0−ay)−(0−ax)(by−ay)=−bxay+axay+axby−axay=axby−bxay,\begin{align*} 2\,\sigma(A, B, O) &= (b_x - a_x)(0 - a_y) - (0 - a_x)(b_y - a_y) \\ &= -b_x a_y + a_x a_y + a_x b_y - a_x a_y \\ &= a_x b_y - b_x a_y, \end{align*}

which is the zz-component of OA⃗×OB⃗\vec{OA} \times \vec{OB}. ■\blacksquare

Corollary. The origin lies inside △ABC\triangle ABC if and only if the three quantities axby−bxaya_x b_y - b_x a_y, bxcy−cxbyb_x c_y - c_x b_y, and cxay−axcyc_x a_y - a_x c_y are all strictly positive or all strictly negative.

Proof. Immediate from Theorem 1 and Lemma 1, noting that multiplication by 2 preserves sign. ■\blacksquare

Editorial

Each triangle can be tested independently by looking only at orientation signs. For vertices AA, BB, and CC, the quantities A×BA \times B, B×CB \times C, and C×AC \times A are the signed areas of the triangles formed with the origin. If these three values are all nonnegative or all nonpositive, then the origin lies inside the triangle (or on its boundary); if both positive and negative signs occur, the origin must lie outside.

That reduces the problem to a straight scan of the data file. For every triangle we compute three determinants, apply the sign test, and increment the answer when the origin passes the test.

Pseudocode

Set the count to zero.

For each triangle with vertices A, B, and C:
    Compute the signed areas of OAB, OBC, and OCA.
    If these three values do not mix positive and negative signs:
        Increase the count.

Return the count.

Complexity Analysis

  • Time. O(N)O(N) where N=1000N = 1000 is the number of triangles. Each triangle requires three cross-product computations and three comparisons, all O(1)O(1).
  • Space. O(1)O(1) auxiliary. Each triangle is processed independently.

Answer

228\boxed{228}

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_102/solution.cpp
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <algorithm>
using namespace std;

/*
 * Problem 102: Triangle Containment
 *
 * Test whether the origin lies inside each triangle using the
 * cross-product sign method: compute the z-components of
 * OA x OB, OB x OC, OC x OA and check that all share the same sign.
 */

bool contains_origin(int x1, int y1, int x2, int y2, int x3, int y3) {
    long long d1 = (long long)x1 * y2 - (long long)x2 * y1;
    long long d2 = (long long)x2 * y3 - (long long)x3 * y2;
    long long d3 = (long long)x3 * y1 - (long long)x1 * y3;
    bool has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
    bool has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);
    return !(has_neg && has_pos);
}

int main() {
    ifstream fin("p102_triangles.txt");
    istream& in = fin.is_open() ? fin : cin;

    int count = 0;
    string line;
    while (getline(in, line)) {
        if (line.empty()) continue;
        replace(line.begin(), line.end(), ',', ' ');
        istringstream iss(line);
        int x1, y1, x2, y2, x3, y3;
        if (iss >> x1 >> y1 >> x2 >> y2 >> x3 >> y3) {
            if (contains_origin(x1, y1, x2, y2, x3, y3))
                count++;
        }
    }

    cout << count << endl;
    return 0;
}