-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
202 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package day370 | ||
|
||
import ( | ||
"log" | ||
"sort" | ||
) | ||
|
||
// DeliveryType enumerates either a pickup or dropoff delivery. | ||
type DeliveryType string | ||
|
||
const ( | ||
Pickup = "pickup" | ||
Dropoff = "dropoff" | ||
) | ||
|
||
// DeliveryAction represents the data supplied to the algorithm. | ||
type DeliveryAction struct { | ||
DeliveryID uint | ||
Timestamp uint | ||
Type DeliveryType | ||
} | ||
|
||
// CourierActiveTime returns the number of seconds the courier was active | ||
// in delivering packages. | ||
// | ||
// Function panics if bad data is discovered e.g. mismatch of dropoff and pickups. | ||
// Runtime is O(N log N) due to sort. | ||
func CourierActiveTime(data []DeliveryAction) uint { | ||
if len(data) == 0 { | ||
return 0 | ||
} | ||
|
||
sort.Slice(data, func(i, j int) bool { | ||
return data[i].Timestamp < data[j].Timestamp | ||
}) | ||
|
||
var answer, startActiveTime uint | ||
|
||
outForDelivery := make(map[uint]DeliveryAction) | ||
d := data[0] | ||
|
||
if d.Type != Pickup { | ||
log.Panicf("mismatched. can't have first chronological item as Dropoff. %v", d) | ||
} | ||
|
||
startActiveTime = d.Timestamp | ||
outForDelivery[d.DeliveryID] = d | ||
|
||
for _, d := range data[1:] { | ||
switch d.Type { | ||
case Pickup: | ||
if len(outForDelivery) == 0 { | ||
startActiveTime = d.Timestamp | ||
} else if _, found := outForDelivery[d.DeliveryID]; found { | ||
log.Panicf("duplicate pickup exists. %v", d) | ||
} | ||
|
||
outForDelivery[d.DeliveryID] = d | ||
case Dropoff: | ||
if _, found := outForDelivery[d.DeliveryID]; !found { | ||
log.Panicf("mismatched. can't have Dropoff without preceding Pickup. %v", d) | ||
} else { | ||
delete(outForDelivery, d.DeliveryID) | ||
if len(outForDelivery) == 0 { | ||
answer += (d.Timestamp - startActiveTime) | ||
} | ||
} | ||
default: | ||
log.Panicf("invalid DeliveryType. %v", d) | ||
} | ||
} | ||
|
||
return answer | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package day370 | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
// nolint | ||
var testcases = []struct { | ||
data []DeliveryAction | ||
activeTime uint | ||
}{ | ||
{nil, 0}, | ||
{ | ||
[]DeliveryAction{ // overlapping deliveries | ||
{1, 10, DeliveryType("pickup")}, | ||
{1, 20, DeliveryType("dropoff")}, | ||
{2, 15, DeliveryType("pickup")}, | ||
{3, 17, DeliveryType("pickup")}, | ||
{3, 22, DeliveryType("dropoff")}, | ||
{2, 30, DeliveryType("dropoff")}, | ||
}, | ||
20, | ||
}, | ||
{ | ||
[]DeliveryAction{ // non-overlapping deliveries | ||
{1, 10, DeliveryType("pickup")}, | ||
{1, 20, DeliveryType("dropoff")}, | ||
{2, 40, DeliveryType("pickup")}, | ||
{3, 100, DeliveryType("pickup")}, | ||
{3, 110, DeliveryType("dropoff")}, | ||
{2, 50, DeliveryType("dropoff")}, | ||
}, | ||
30, | ||
}, | ||
} | ||
|
||
func TestCourierActiveTime(t *testing.T) { | ||
t.Parallel() | ||
|
||
for _, tc := range testcases { | ||
if activeTime := CourierActiveTime(tc.data); activeTime != tc.activeTime { | ||
t.Errorf("Expected %d, got %d", tc.activeTime, activeTime) | ||
} | ||
} | ||
} | ||
|
||
func TestCourierActiveTimeDropOffBeforePickup(t *testing.T) { | ||
t.Parallel() | ||
|
||
data := []DeliveryAction{ | ||
{1, 1573280047, DeliveryType("pickup")}, | ||
{1, 1570320725, DeliveryType("dropoff")}, | ||
{2, 1570321092, DeliveryType("pickup")}, | ||
{3, 1570321212, DeliveryType("pickup")}, | ||
{3, 1570322352, DeliveryType("dropoff")}, | ||
{2, 1570323012, DeliveryType("dropoff")}, | ||
} | ||
|
||
defer func() { | ||
if err := recover(); err == nil { | ||
t.Errorf("Expected a panic due to the first event by time being a Dropoff") | ||
} | ||
}() | ||
|
||
CourierActiveTime(data) | ||
} | ||
|
||
func TestCourierActiveTimeDuplicatePickup(t *testing.T) { | ||
t.Parallel() | ||
|
||
data := []DeliveryAction{ | ||
{3, 30, DeliveryType("pickup")}, | ||
{3, 40, DeliveryType("pickup")}, | ||
} | ||
|
||
defer func() { | ||
if err := recover(); err == nil { | ||
t.Errorf("Expected a panic due to a duplicate pickup for the same delivery ID") | ||
} | ||
}() | ||
|
||
CourierActiveTime(data) | ||
} | ||
|
||
func TestCourierActiveTimeDropoffBeforePickup(t *testing.T) { | ||
t.Parallel() | ||
|
||
data := []DeliveryAction{ | ||
{1, 10, DeliveryType("pickup")}, | ||
{1, 20, DeliveryType("dropoff")}, | ||
{3, 30, DeliveryType("dropoff")}, | ||
} | ||
|
||
defer func() { | ||
if err := recover(); err == nil { | ||
t.Errorf("Expected a panic due to a dropoff only. no pickup.") | ||
} | ||
}() | ||
|
||
CourierActiveTime(data) | ||
} | ||
|
||
func TestCourierActiveTimeInvalidDeliveryType(t *testing.T) { | ||
t.Parallel() | ||
|
||
data := []DeliveryAction{ | ||
{3, 30, DeliveryType("pickup")}, | ||
{4, 40, DeliveryType("gonnapanic")}, | ||
} | ||
|
||
defer func() { | ||
if err := recover(); err == nil { | ||
t.Errorf("Expected a panic due to a duplicate pickup for the same delivery ID") | ||
} | ||
}() | ||
|
||
CourierActiveTime(data) | ||
} | ||
|
||
func BenchmarkCourierActiveTime(b *testing.B) { | ||
for i := 0; i < b.N; i++ { | ||
for _, tc := range testcases { | ||
CourierActiveTime(tc.data) | ||
} | ||
} | ||
} |