Proportional representation with droop quota and quota rule

Hello,
I was running propertybased tests and it seems to have found a situation where the Droop quota violated the quota rule. As a largest remainder method, I thought the Droop quota will always satisfy the quota rule. I don't think it is my code being buggy because I reached the same results when calculating by hand. You can also use this unrelated calculator and it gives the same results. In fact, its code uses my algorithm. I found another project which I didn't manage to compile, but the code should also give the same results.
I am aware of the mathematical proofs behind the method and the quota rule, so I hope all three of us made a mistake!
Consider this election with 4 parties contesting 360 seats, with the following vote counts
p1, p2, p3, p4 = 885292, 50089, 1536, 87859 house_size = 360 sum == 1024776
Calculate the droop quota
quota = 1 + floor(sum / (1 + house_size)) quota == 2839
Divide each party's votes by the quota
p1 / quota = 311.83233532934133 p2 / quota = 17.643184219795703 p3 / quota = 0.5410355759070095 p4 / quota = 30.94716449454033
Give each party their automatic seats based on the floor of the above:
party seats 1 311 2 17 3 0 4 30 total 358 There are 2 seats remaining, so give one seat to the two parties with the largest remainder  they are party #3 (0.94716449454033) and party #1 (0.83233532934133)
The election result is thus:
party seats 1 312 2 17 3 0 4 31 total 360 The problem with this result is that party #1's seats violated the quota rule. The lower and upper quotas are the vote percentages times the house size:
expr result lower quota upper quota p1 / sum * house_size 310.99978922223 310 311 p2 / sum * house_size 17.596079533478534 17 18 p3 / sum * house_size 0.5395910911262558 0 1 p4 / sum * house_size 30.86454015316518 30 31 Party #1 received 312 seats, which violates the quota rule. The additional seat could be awarded to party #2 or #3 without violating the quota rule. Notably, party #1 was automatically allocated its upper quota, and gained another one due to its large remainder.
I'm following the rules on the Wikipedia page for the largest remainder method  as do the two other implementations. You can see my code here. Please do leave a comment if you have an insight. Thank you.
PS: I'd post this to the proportional representation category but there was no new topic button
Edit: here are some other parameters that appear to violate quota:
house_size = 72, p1 = 80183, p2 = 34027, p3 = 403586, p4 = 30472 house_size = 144, p1 = 80183, p2 = 34027, p3 = 803270, p4 = 7888 house_size = 216, p1 = 35570, p2 = 24675, p3 = 798357, p4 = 30291 house_size = 288, p1 = 80183, p2 = 34027, p3 = 705602, p4 = 23398
They all appear to share some common characteristics: the largest party has over 80% of the vote; their vote share has a large remainder, and the floor of their vote share is their upper quota. In other words, to construct this scenario, find
p
such thatfloor(p) == upper_quota(p)
andremainder(p)
is one of the largest compared to the other parties. 
@akazukin5151 said in Proportional representation with droop quota and quota rule:
The lower and upper quotas are the vote percentages times the house size:
If you are using Droop quotas to compute the seats then you must also use the Droop quota to compute the upper/lower quotas. What you have done is use the Hare quota to define the upper/lower quotas but then use the Droop quota to fill the seats.

Also Droop Quota isn't a method. You can have e.g. largest remainder or highest averages methods that use the Droop quota.

@andydienes Oh, that makes sense. How do I use the droop quota to calculate the lower and upper quota though? I can sort of see the similarities between
p1 / sum
and the hare quota (total_votes/total_seats
). But if I substitutep1
intototal_votes
andsum
(of all votes) intototal_seats
for droop, I get1 + floor(p1 / (1 + sum))
, which will always be greater than 1. Even if the1+
andfloor
is dropped, I still get 311 for party #1's upper quota. Thank you so much for your help! 
floor( vote / droop_quota )
is the lower quota,ceil( votes / droop_quota )
is the upper quota.