Integer Angled Quadrilaterals
Let ABCD be a convex quadrilateral with diagonals AC and BD. At each vertex, the diagonal makes an angle with each of the two sides, creating eight corner angles. How many non-similar convex quadri...
Problem Statement
This archive keeps the full statement, math, and original media on the page.
Let $ABCD$ be a convex quadrilateral, with diagonals $AC$ and $BD$. At each vertex the diagonal makes an angle with each of the two sides, creating eight corner angles.

For example, at vertex $A$, the two angles are $CAD$, $CAB$.
We call such a quadrilateral for which all eight corner angles have integer values when measured in degrees an "integer angled quadrilateral". An example of an integer angled quadrilateral is a square, where all eight corner angles are $45^\circ$. Another example is given by $DAC = 20^\circ$, $BAC = 60^\circ$, $ABD = 50^\circ$, $CBD = 30^\circ$, $BCA = 40^\circ$, $DCA = 30^\circ$, $CDB = 80^\circ$, $ADB = 50^\circ$.
What is the total number of non-similar integer angled quadrilaterals?
Note: In your calculations you may assume that a calculated angle is integral if it is within a tolerance of $10^{-9}$ of an integer value.
Problem 177: Integer Angled Quadrilaterals
Mathematical Development
Setup
The two diagonals AC and BD intersect at point P inside the convex quadrilateral, creating four triangles. Label the eight sub-angles:
- At A: (toward B) and (toward D), split by diagonal AC
- At B: (toward A) and (toward C), split by diagonal BD
- At C: (toward B) and (toward D), split by diagonal AC
- At D: (toward C) and (toward A), split by diagonal BD
Constraints from Triangle Angle Sums
Let be the angle at the intersection point P in triangle APB. Then from the four sub-triangles:
Geometric Closure Condition
For the quadrilateral to close (the two diagonals to intersect consistently), the sine rule in each sub-triangle yields the crucial constraint:
This is derived from the requirement that the four segments AP, BP, CP, DP have consistent lengths across all four sub-triangles.
Free Variables
Given , the 8 angles are determined by 4 free variables: (with derived). The closure condition reduces this to 3 effective degrees of freedom. For computation, we fix and solve for :
where and .
Ranges
- : 2 to 178
- : 1 to (so that )
- : 1 to (so that )
- : solved from closure, must be integer in
Convexity
The quadrilateral angles , , , must all be less than 180.
Symmetry Reduction
The dihedral group of order 8 acts on the quadrilateral via vertex relabelings. The generators are:
Rotation : ABCD BCDA maps
Reflection : ABCD ADCB maps
These generate 8 symmetries. Two 8-tuples in the same orbit represent similar quadrilaterals. We canonicalize each valid tuple under this group and count distinct canonical forms.
Editorial
The implementation treats the intersection angle and three of the corner sub-angles as free variables. Once , , , and are chosen, the companion angles , , and are forced by the triangle-sum identities, and the closure equation determines the only plausible value of . Instead of blindly trying every integer , the code uses the trigonometric identity to compute a floating-point estimate with atan2, then tests only the nearby integer candidates.
That leaves two final filters. The sine-product equation must still hold to high precision, and the resulting quadrilateral angles must all stay below 180 degrees to ensure convexity. Every surviving 8-tuple is then canonicalized under the dihedral symmetry group of the quadrilateral, so all rotated or reflected copies collapse to a single representative before counting.
Pseudocode
Precompute the sine values for all integer angles from 0 to 180 degrees.
Prepare the eight symmetry permutations of the quadrilateral labels.
For each intersection angle $p$ from 2 to 178:
For each admissible triple $(\alpha, \delta, \zeta)$:
derive $\gamma$, $\varepsilon$, and $\eta$ from the angle-sum constraints.
Use the closure equation to estimate $\theta$ with `atan2`.
Check only the small set of nearby integer values of $\theta`.
For each such candidate:
derive $\beta = p - \theta$.
verify the sine-product closure condition numerically.
verify that all four full quadrilateral angles are strictly below 180.
Form the 8-tuple of sub-angles.
Replace it by the lexicographically smallest image under the dihedral symmetries.
Insert that canonical tuple into a set.
Return the size of the set.
Complexity Analysis
The outer loops cover all admissible values of , , , and , but the angle constraints shrink the practical search far below the naive quartic bound. For each surviving triple, solving for , checking closure, and canonicalizing under the eight symmetries all take constant time. The total runtime is therefore dominated by the nested enumeration and is practical in a few seconds.
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#include <bits/stdc++.h>
using namespace std;
// Problem 177: Integer Angled Quadrilaterals
// Fixed range: alpha and zeta can go up to 179-p (not 178-p).
// Convexity requires A,B,C,D < 180, not the sub-angle pair sums.
int main() {
double sinv[181];
for (int i = 0; i <= 180; i++) sinv[i] = sin(i * M_PI / 180.0);
typedef array<int,8> T8;
int r_perm[8] = {3,2,4,5,6,7,1,0};
int s_perm[8] = {1,0,7,6,5,4,3,2};
int perms[8][8];
for (int i = 0; i < 8; i++) perms[0][i] = i;
for (int i = 0; i < 8; i++) perms[1][i] = r_perm[i];
for (int k = 2; k <= 3; k++)
for (int i = 0; i < 8; i++) perms[k][i] = perms[k-1][r_perm[i]];
for (int i = 0; i < 8; i++) perms[4][i] = s_perm[i];
for (int k = 5; k <= 7; k++)
for (int i = 0; i < 8; i++) perms[k][i] = perms[4][perms[k-4][i]];
auto canonical = [&](const T8& t) -> T8 {
T8 best = t;
for (int sym = 1; sym < 8; sym++) {
T8 tt;
for (int i = 0; i < 8; i++) tt[i] = t[perms[sym][i]];
if (tt < best) best = tt;
}
return best;
};
set<T8> seen;
for (int p = 2; p <= 178; p++) {
double sp = sinv[p], cp = cos(p * M_PI / 180.0);
int max_az = 179 - p; // FIXED: was 178-p
int max_d = p - 1;
for (int alpha = 1; alpha <= max_az; alpha++) {
int gamma = 180 - p - alpha;
for (int delta = 1; delta <= max_d; delta++) {
int epsilon = p - delta;
for (int zeta = 1; zeta <= max_az; zeta++) {
int eta = 180 - p - zeta;
double L = sinv[alpha] * sinv[delta] * sinv[zeta];
double R = sinv[gamma] * sinv[epsilon] * sinv[eta];
double y = R * sp;
double x = L + R * cp;
double theta_exact = atan2(y, x) * 180.0 / M_PI;
if (theta_exact < 0.5 || theta_exact > max_d + 0.5) continue;
int t_round = (int)round(theta_exact);
for (int theta = max(1, t_round - 1); theta <= min(max_d, t_round + 1); theta++) {
int beta = p - theta;
if (beta < 1) continue;
double lhs = sinv[alpha] * sinv[delta] * sinv[zeta] * sinv[theta];
double rhs = sinv[beta] * sinv[gamma] * sinv[epsilon] * sinv[eta];
double rel = fabs(lhs - rhs) / max(fabs(lhs), fabs(rhs));
if (rel > 1e-9) continue;
// Convexity: quad angles < 180
int A = alpha + beta, B = gamma + delta, C = epsilon + zeta, D = eta + theta;
if (A >= 180 || B >= 180 || C >= 180 || D >= 180) continue;
T8 t = {alpha, beta, gamma, delta, epsilon, zeta, eta, theta};
seen.insert(canonical(t));
}
}
}
}
}
cout << seen.size() << endl;
return 0;
}
"""
Problem 177: Integer Angled Quadrilaterals
Count non-similar convex quadrilaterals ABCD where both diagonals split
all four angles into integer-degree sub-angles.
8 sub-angles: alpha=BAC, beta=CAD, gamma=ABD, delta=DBC,
epsilon=BCA, zeta=ACD, eta=CDB, theta=BDA
Constraints from diagonal intersection angle p:
alpha+gamma = 180-p, delta+epsilon = p, zeta+eta = 180-p, theta+beta = p
Closure: sin(alpha)*sin(delta)*sin(zeta)*sin(theta) = sin(beta)*sin(gamma)*sin(epsilon)*sin(eta)
Symmetry group (dihedral-8) reduces count by factor ~8.
"""
import math
sinv = [0.0] * 181
for i in range(181):
sinv[i] = math.sin(i * math.pi / 180.0)
# Permutations for dihedral-8 symmetry group
# r: rotation ABCD->BCDA
# s: reflection ABCD->ADCB
r_perm = [3, 2, 4, 5, 6, 7, 1, 0]
s_perm = [1, 0, 7, 6, 5, 4, 3, 2]
perms = [None] * 8
perms[0] = list(range(8))
perms[1] = r_perm[:]
for k in range(2, 4):
perms[k] = [perms[k-1][r_perm[i]] for i in range(8)]
perms[4] = s_perm[:]
for k in range(5, 8):
perms[k] = [perms[4][perms[k-4][i]] for i in range(8)]
def canonical(t):
best = t
for s in range(1, 8):
tt = tuple(t[perms[s][i]] for i in range(8))
if tt < best:
best = tt
return best
seen = set()
for p in range(2, 179):
sp = sinv[p]
cp = math.cos(p * math.pi / 180.0)
max_az = 179 - p
max_d = p - 1
for alpha in range(1, max_az + 1):
gamma = 180 - p - alpha
for delta in range(1, max_d + 1):
epsilon = p - delta
for zeta in range(1, max_az + 1):
eta = 180 - p - zeta
L = sinv[alpha] * sinv[delta] * sinv[zeta]
R = sinv[gamma] * sinv[epsilon] * sinv[eta]
y = R * sp
x = L + R * cp
theta_exact = math.atan2(y, x) * 180.0 / math.pi
if theta_exact < 0.5 or theta_exact > max_d + 0.5:
continue
t_round = round(theta_exact)
for theta in range(max(1, t_round - 1), min(max_d, t_round + 1) + 1):
beta = p - theta
if beta < 1:
continue
lhs = sinv[alpha] * sinv[delta] * sinv[zeta] * sinv[theta]
rhs = sinv[beta] * sinv[gamma] * sinv[epsilon] * sinv[eta]
mx = max(abs(lhs), abs(rhs))
if mx == 0:
continue
if abs(lhs - rhs) / mx > 1e-9:
continue
A = alpha + beta
B = gamma + delta
C = epsilon + zeta
D = eta + theta
if A >= 180 or B >= 180 or C >= 180 or D >= 180:
continue
t = (alpha, beta, gamma, delta, epsilon, zeta, eta, theta)
seen.add(canonical(t))
print(len(seen))