diff --git a/model_cluster.go b/model_cluster.go index e431cdc..a5ceaa2 100644 --- a/model_cluster.go +++ b/model_cluster.go @@ -226,13 +226,13 @@ func (l *clusterImpl) estimateDeltaScore( return deltaScore, constNoPositionsHint } - candidate := stopPosition.stop() + candidate := stopPosition.Stop() var c *centroidData if asConstraint { - c = vehicle.last().ConstraintData(l).(*centroidData) + c = vehicle.Last().ConstraintData(l).(*centroidData) } else { - c = vehicle.last().ObjectiveData(l).(*centroidData) + c = vehicle.Last().ObjectiveData(l).(*centroidData) } centroid := c.location @@ -249,7 +249,7 @@ func (l *clusterImpl) estimateDeltaScore( continue } centroidOtherVehicle := otherVehicle. - last(). + Last(). ConstraintData(l).(*centroidData).location if haversineDistance( @@ -285,12 +285,11 @@ func (l *clusterImpl) getSolutionStops(vehicle SolutionVehicle) []SolutionStop { } func (l *clusterImpl) Value(solutionStop Solution) float64 { sum := 0.0 - for _, v := range solutionStop.(*solutionImpl).vehiclesMutable() { - vehicle := v.(solutionVehicleImpl) + for _, vehicle := range solutionStop.(*solutionImpl).vehiclesMutable() { if vehicle.IsEmpty() { continue } - sum += vehicle.last().ObjectiveData(l).(*centroidData).compactness + sum += vehicle.Last().ObjectiveData(l).(*centroidData).compactness } return sum } diff --git a/model_constraint_maximum_duration.go b/model_constraint_maximum_duration.go index bff13cc..5d5fb3e 100644 --- a/model_constraint_maximum_duration.go +++ b/model_constraint_maximum_duration.go @@ -60,7 +60,7 @@ func (l *maximumDurationConstraintImpl) EstimateIsViolated( maximumValue := l.maximum.Value(vehicleType, nil, nil) - startValue := vehicle.first().StartValue() + startValue := vehicle.First().StartValue() previous, _ := moveImpl.previous() endValue := previous.EndValue() diff --git a/model_constraint_maximum_stops.go b/model_constraint_maximum_stops.go index 6839058..17b3091 100644 --- a/model_constraint_maximum_stops.go +++ b/model_constraint_maximum_stops.go @@ -57,7 +57,7 @@ func (l *maximumStopsConstraintImpl) EstimateIsViolated( stopPositions := moveImpl.stopPositions nrStopsToBeAddedToSolution := len(stopPositions) - beforeStop := stopPositions[len(stopPositions)-1].next() + beforeStop := stopPositions[len(stopPositions)-1].Next() vehicle := beforeStop.vehicle() vehicleType := vehicle.ModelVehicle().VehicleType().Index() diff --git a/model_constraint_maximum_travel_duration.go b/model_constraint_maximum_travel_duration.go index 2d28326..1e36f5a 100644 --- a/model_constraint_maximum_travel_duration.go +++ b/model_constraint_maximum_travel_duration.go @@ -94,9 +94,8 @@ func (l *maximumTravelDurationConstraintImpl) EstimateIsViolated( } func (l *maximumTravelDurationConstraintImpl) DoesVehicleHaveViolations(vehicle SolutionVehicle) bool { - vehicleImpl := vehicle.(solutionVehicleImpl) - return vehicleImpl.last().CumulativeTravelDurationValue() > - l.maximum.Value(vehicleImpl.ModelVehicle().VehicleType(), nil, nil) + return vehicle.Last().CumulativeTravelDurationValue() > + l.maximum.Value(vehicle.ModelVehicle().VehicleType(), nil, nil) } func (l *maximumTravelDurationConstraintImpl) IsTemporal() bool { diff --git a/model_constraint_no_mix.go b/model_constraint_no_mix.go index bcf6b1f..a508cb8 100644 --- a/model_constraint_no_mix.go +++ b/model_constraint_no_mix.go @@ -349,19 +349,19 @@ func (l *noMixConstraintImpl) EstimateIsViolated( move SolutionMoveStops, ) (isViolated bool, stopPositionsHint StopPositionsHint) { moveImpl := move.(*solutionMoveStopsImpl) - _, hasRemoveMixItem := l.remove[moveImpl.stopPositions[0].stop().ModelStop()] + _, hasRemoveMixItem := l.remove[moveImpl.stopPositions[0].Stop().ModelStop()] if hasRemoveMixItem { return true, constNoPositionsHint } - previousStopImp := moveImpl.stopPositions[0].previous() + previousStopImp := moveImpl.stopPositions[0].Previous() previousNoMixData := previousStopImp.ConstraintData(l).(*noMixSolutionStopData) contentName := previousNoMixData.content.Name contentQuantity := previousNoMixData.content.Quantity deltaQuantity := 0 - insertMixItem, hasInsertMixItem := l.insert[moveImpl.stopPositions[0].stop().ModelStop()] + insertMixItem, hasInsertMixItem := l.insert[moveImpl.stopPositions[0].Stop().ModelStop()] if hasInsertMixItem { if contentName != insertMixItem.Name && previousNoMixData.content.Quantity != 0 { return true, constNoPositionsHint @@ -377,14 +377,14 @@ func (l *noMixConstraintImpl) EstimateIsViolated( } for idx := 1; idx < len(moveImpl.stopPositions); idx++ { - previousStopImp = moveImpl.stopPositions[idx].previous() + previousStopImp = moveImpl.stopPositions[idx].Previous() if previousStopImp.IsPlanned() { previousNoMixData = previousStopImp.ConstraintData(l).(*noMixSolutionStopData) if previousNoMixData.tour != tour || previousNoMixData.content.Name != contentName { return true, constNoPositionsHint } } - insertMixItem, hasInsertMixItem = l.insert[moveImpl.stopPositions[idx].stop().ModelStop()] + insertMixItem, hasInsertMixItem = l.insert[moveImpl.stopPositions[idx].Stop().ModelStop()] if hasInsertMixItem { if contentName != insertMixItem.Name { return true, constNoPositionsHint @@ -392,7 +392,7 @@ func (l *noMixConstraintImpl) EstimateIsViolated( deltaQuantity += insertMixItem.Quantity continue } - removeMixItem, hasRemoveMixItem := l.remove[moveImpl.stopPositions[idx].stop().ModelStop()] + removeMixItem, hasRemoveMixItem := l.remove[moveImpl.stopPositions[idx].Stop().ModelStop()] if hasRemoveMixItem { if contentName != removeMixItem.Name || contentQuantity+deltaQuantity < removeMixItem.Quantity { return true, constNoPositionsHint diff --git a/model_latest.go b/model_latest.go index d52ce9b..ffd8bbc 100644 --- a/model_latest.go +++ b/model_latest.go @@ -190,8 +190,8 @@ func (l *latestImpl) Value(s Solution) float64 { solution := s.(*solutionImpl) value := 0.0 for _, vehicle := range solution.vehicles { - solutionStop := vehicle.first().Next() - lastSolutionStop := vehicle.last() + solutionStop := vehicle.First().Next() + lastSolutionStop := vehicle.Last() for { latenessFactor := l.latenessFactor.Value( nil, @@ -241,7 +241,7 @@ func (l *latestImpl) estimateDeltaScore( first := true arrival, start, end := 0.0, 0.0, 0.0 - previousStop := vehicle.first().ModelStop() + previousStop := vehicle.First().ModelStop() generator := newSolutionStopGenerator(*move, false, true) defer generator.release() diff --git a/model_maximum.go b/model_maximum.go index f08b3c5..a5d9268 100644 --- a/model_maximum.go +++ b/model_maximum.go @@ -208,7 +208,7 @@ func (l *maximumImpl) EstimateIsViolated( // level at the end of the vehicle. We can only do this if the expression // is a stop expression. if l.hasStopExpressionAndNoNegativeValues { - cumulativeValue := vehicle.last().CumulativeValue(expression) + cumulativeValue := vehicle.Last().CumulativeValue(expression) if cumulativeValue+l.deltas[moveImpl.planUnit.modelPlanStopsUnit.Index()] > maximum { return true, constSkipVehiclePositionsHint @@ -241,7 +241,7 @@ func (l *maximumImpl) EstimateIsViolated( if !l.hasNegativeValues { violated := level-previousStop.CumulativeValue(l.Expression())+ - vehicle.last().CumulativeValue(l.Expression()) > maximum + vehicle.Last().CumulativeValue(l.Expression()) > maximum return violated, constNoPositionsHint } @@ -304,7 +304,7 @@ func (l *maximumImpl) EstimateDeltaValue( vehicle := moveImpl.vehicle() - hasViolation := vehicle.last().ObjectiveData(l).(*maximumObjectiveDate).hasViolation + hasViolation := vehicle.Last().ObjectiveData(l).(*maximumObjectiveDate).hasViolation vehicleType := vehicle.ModelVehicle().VehicleType() maximum := l.maximumByVehicleType[vehicleType.Index()] @@ -325,7 +325,7 @@ func (l *maximumImpl) EstimateDeltaValue( // level at the end of the vehicle. We can only do this if the expression // is a stop expression. if l.hasStopExpressionAndNoNegativeValues { - cumulativeValue := vehicle.last().CumulativeValue(l.resourceExpression) + cumulativeValue := vehicle.Last().CumulativeValue(l.resourceExpression) returnValue := 0.0 excess := cumulativeValue + l.deltas[moveImpl.planUnit.modelPlanStopsUnit.Index()] - maximum @@ -394,7 +394,7 @@ func (l *maximumImpl) Value( maximum := l.maximumByVehicleType[vehicleType.Index()] if l.hasStopExpressionAndNoNegativeValues { - cumulativeValue := vehicle.last().CumulativeValue(l.resourceExpression) + cumulativeValue := vehicle.Last().CumulativeValue(l.resourceExpression) excess := cumulativeValue - maximum if excess > 0 { score += excess diff --git a/model_objective_earliness.go b/model_objective_earliness.go index 759d49c..e7291f3 100644 --- a/model_objective_earliness.go +++ b/model_objective_earliness.go @@ -123,7 +123,7 @@ func (l *earlinessObjectiveImpl) EstimateDeltaValue( // Init data first := true arrival, start, end := 0.0, 0.0, 0.0 - previousStop := vehicle.first() + previousStop := vehicle.First() // Get sequence starting with the first stop prior to the first stop to be // inserted. diff --git a/model_objective_travelduration.go b/model_objective_travelduration.go index dfce85b..0b4dbea 100644 --- a/model_objective_travelduration.go +++ b/model_objective_travelduration.go @@ -28,7 +28,7 @@ func (t *travelDurationObjectiveImpl) Value(solution Solution) float64 { score := 0.0 for _, vehicle := range solutionImp.vehicles { - score += vehicle.last().CumulativeTravelDurationValue() + score += vehicle.Last().CumulativeTravelDurationValue() } return score } diff --git a/model_objective_vehicles_duration.go b/model_objective_vehicles_duration.go index b66ff58..023e883 100644 --- a/model_objective_vehicles_duration.go +++ b/model_objective_vehicles_duration.go @@ -58,7 +58,7 @@ func (t *vehiclesDurationObjectiveImpl) EstimateDeltaValue( first := true end := 0.0 - previousStop := vehicle.first() + previousStop := vehicle.First() generator := newSolutionStopGenerator(*solutionMoveStops, false, isDependentOnTime) defer generator.release() @@ -83,7 +83,7 @@ func (t *vehiclesDurationObjectiveImpl) EstimateDeltaValue( nextmove, _ := solutionMoveStops.next() if nextmove.IsLast() || isDependentOnTime { - return end - vehicle.last().EndValue() + return end - vehicle.Last().EndValue() } for solutionStop := nextmove.Next(); !solutionStop.IsLast(); solutionStop = solutionStop.Next() { @@ -99,7 +99,7 @@ func (t *vehiclesDurationObjectiveImpl) EstimateDeltaValue( } } - last := vehicle.last() + last := vehicle.Last() _, _, _, end = vehicleType.TemporalValues( end, last.Previous().ModelStop(), diff --git a/solution.go b/solution.go index 1c8c9ca..2e67eb0 100644 --- a/solution.go +++ b/solution.go @@ -129,7 +129,7 @@ func NewSolution( model: m, vehicleIndices: make([]int, 0, nrVehicles), - vehicles: make([]solutionVehicleImpl, 0, nrVehicles), + vehicles: make([]SolutionVehicle, 0, nrVehicles), solutionVehicles: make([]SolutionVehicle, 0, nrVehicles), first: make([]int, 0, nrVehicles), last: make([]int, 0, nrVehicles), @@ -395,7 +395,7 @@ func (s *solutionImpl) addInitialSolution(m Model) error { PlanUnitLoop: for _, planUnit := range planUnits { stopPositions := make(StopPositions, 0, len(planUnit.SolutionStops())) - previousStop := solutionVehicle.first() + previousStop := solutionVehicle.First() solutionPlanUnit := s.unwrapRootPlanUnit(planUnit) allPlanUnits[solutionPlanUnit] = true @@ -455,7 +455,7 @@ func (s *solutionImpl) addInitialSolution(m Model) error { newStopPosition( previousStop, solutionStop, - solutionVehicle.last(), + solutionVehicle.Last(), ), ) } @@ -510,7 +510,7 @@ func (s *solutionImpl) addInitialSolution(m Model) error { ) } for _, position := range move.(*solutionMoveStopsImpl).stopPositions { - position.stop().detach() + position.Stop().detach() } infeasiblePlanUnits[solutionPlanUnit] = true continue @@ -594,7 +594,7 @@ type solutionImpl struct { vehicleIndices []int // TODO: explore if vehicles should rather be interfaces, then we can avoid creating new vehicles on the fly - vehicles []solutionVehicleImpl + vehicles []SolutionVehicle solutionVehicles []SolutionVehicle start []float64 slack []float64 @@ -664,17 +664,17 @@ func (s *solutionImpl) SolutionVehicle(vehicle ModelVehicle) SolutionVehicle { if solutionVehicle, ok := s.solutionVehicle(vehicle); ok { return solutionVehicle } - return nil + return SolutionVehicle{} } -func (s *solutionImpl) solutionVehicle(vehicle ModelVehicle) (solutionVehicleImpl, bool) { +func (s *solutionImpl) solutionVehicle(vehicle ModelVehicle) (SolutionVehicle, bool) { if vehicle != nil { - return solutionVehicleImpl{ + return SolutionVehicle{ index: vehicle.Index(), solution: s, }, true } - return solutionVehicleImpl{}, false + return SolutionVehicle{}, false } func (s *solutionImpl) Copy() Solution { @@ -834,7 +834,7 @@ func (s *solutionImpl) newVehicle( modelVehicle ModelVehicle, ) (SolutionVehicle, error) { if modelVehicle == nil { - return nil, fmt.Errorf("modelVehicle is nil") + return SolutionVehicle{}, fmt.Errorf("modelVehicle is nil") } model := s.model.(*modelImpl) @@ -858,11 +858,11 @@ func (s *solutionImpl) newVehicle( modelVehicle.Last().Index(), ) s.vehicleIndices = append(s.vehicleIndices, modelVehicle.Index()) - s.vehicles = append(s.vehicles, solutionVehicleImpl{ + s.vehicles = append(s.vehicles, SolutionVehicle{ index: modelVehicle.Index(), solution: s, }) - s.solutionVehicles = append(s.solutionVehicles, solutionVehicleImpl{ + s.solutionVehicles = append(s.solutionVehicles, SolutionVehicle{ index: modelVehicle.Index(), solution: s, }) @@ -903,10 +903,10 @@ func (s *solutionImpl) newVehicle( constraint, _, err := s.isFeasible(len(s.stop)-2, true) if err != nil { - return nil, err + return SolutionVehicle{}, err } if constraint != nil { - return nil, fmt.Errorf("failed creating new vehicle: %v", constraint) + return SolutionVehicle{}, fmt.Errorf("failed creating new vehicle: %v", constraint) } return toSolutionVehicle(s, len(s.vehicles)-1), nil @@ -1083,7 +1083,7 @@ func (s *solutionImpl) BestMove(ctx context.Context, planUnit SolutionPlanUnit) return bestMove } - solutionVehicle := solutionVehicleImpl{ + solutionVehicle := SolutionVehicle{ index: -1, solution: s, } diff --git a/solution_move_stops.go b/solution_move_stops.go index 44f941a..4e761ef 100644 --- a/solution_move_stops.go +++ b/solution_move_stops.go @@ -130,13 +130,13 @@ func (m *solutionMoveStopsImpl) Solution() Solution { func (m *solutionMoveStopsImpl) Vehicle() SolutionVehicle { if len(m.stopPositions) == 0 { - return nil + return SolutionVehicle{} } - return m.stopPositions[len(m.stopPositions)-1].next().Vehicle() + return m.stopPositions[len(m.stopPositions)-1].Next().Vehicle() } -func (m *solutionMoveStopsImpl) vehicle() solutionVehicleImpl { - return m.stopPositions[len(m.stopPositions)-1].next().vehicle() +func (m *solutionMoveStopsImpl) vehicle() SolutionVehicle { + return m.stopPositions[len(m.stopPositions)-1].Next().vehicle() } func (m *solutionMoveStopsImpl) Next() SolutionStop { @@ -150,7 +150,7 @@ func (m *solutionMoveStopsImpl) next() (SolutionStop, bool) { if len(m.stopPositions) == 0 { return SolutionStop{}, false } - return m.stopPositions[len(m.stopPositions)-1].next(), true + return m.stopPositions[len(m.stopPositions)-1].Next(), true } func (m *solutionMoveStopsImpl) Previous() SolutionStop { @@ -165,7 +165,7 @@ func (m *solutionMoveStopsImpl) previous() (SolutionStop, bool) { if len(m.stopPositions) == 0 { return SolutionStop{}, false } - return m.stopPositions[0].previous(), true + return m.stopPositions[0].Previous(), true } func (m *solutionMoveStopsImpl) Execute(_ context.Context) (bool, error) { @@ -198,7 +198,7 @@ func (m *solutionMoveStopsImpl) Execute(_ context.Context) (bool, error) { } for _, position := range m.stopPositions { - position.stop().detach() + position.Stop().detach() } constraint, _, err := m.planUnit.solution().isFeasible(startPropagate, true) @@ -224,9 +224,9 @@ func (m *solutionMoveStopsImpl) attach() (int, error) { startPropagate := -1 for i := len(m.stopPositions) - 1; i >= 0; i-- { stopPosition := m.stopPositions[i] - m.planUnit.solutionStops[i] = stopPosition.stop() - beforeStop := stopPosition.next() - if stopPosition.stop().IsPlanned() { + m.planUnit.solutionStops[i] = stopPosition.Stop() + beforeStop := stopPosition.Next() + if stopPosition.Stop().IsPlanned() { return -1, fmt.Errorf( "stop %v is already planned", stopPosition.Stop(), @@ -238,7 +238,7 @@ func (m *solutionMoveStopsImpl) attach() (int, error) { beforeStop, ) } - startPropagate = stopPosition.stop().attach( + startPropagate = stopPosition.Stop().attach( beforeStop.PreviousIndex(), ) } @@ -321,7 +321,7 @@ func (m *solutionMoveStopsImpl) TakeBest(that SolutionMove) SolutionMove { func (m *solutionMoveStopsImpl) deltaStopTravelDurationValue( vehicleType ModelVehicleType, ) float64 { - if len(m.stopPositions) == 0 || m.stopPositions[0].stop().IsPlanned() { + if len(m.stopPositions) == 0 || m.stopPositions[0].Stop().IsPlanned() { return 0 } deltaStopDurationValue := 0.0 @@ -329,12 +329,12 @@ func (m *solutionMoveStopsImpl) deltaStopTravelDurationValue( vehicleTravelDuration := vehicleType.TravelDurationExpression() vehicleDuration := vehicleType.DurationExpression() for _, stopPosition := range m.stopPositions { - modelStop := stopPosition.stop().ModelStop() - nextStop := stopPosition.next().ModelStop() - previousStop := stopPosition.previous().ModelStop() - if stopPosition.next().IsPlanned() { - deltaStopDurationValue -= stopPosition.next().DurationValue() - travelDuration -= stopPosition.next().TravelDurationValue() + modelStop := stopPosition.Stop().ModelStop() + nextStop := stopPosition.Next().ModelStop() + previousStop := stopPosition.Previous().ModelStop() + if stopPosition.Next().IsPlanned() { + deltaStopDurationValue -= stopPosition.Next().DurationValue() + travelDuration -= stopPosition.Next().TravelDurationValue() travelDuration += vehicleTravelDuration.Value( vehicleType, modelStop, @@ -361,7 +361,7 @@ func (m *solutionMoveStopsImpl) deltaStopTravelDurationValue( } func (m *solutionMoveStopsImpl) deltaTravelDurationValue() float64 { - if len(m.stopPositions) == 0 || m.stopPositions[0].stop().IsPlanned() { + if len(m.stopPositions) == 0 || m.stopPositions[0].Stop().IsPlanned() { return 0 } @@ -373,7 +373,7 @@ func (m *solutionMoveStopsImpl) deltaTravelDurationValue() float64 { if isDependentOnTime { if len(m.stopPositions) == 1 { - solutionStop := m.stopPositions[0].stop() + solutionStop := m.stopPositions[0].Stop() previousStop, _ := m.previous() departure := previousStop.EndValue() fromDuration, _, _, _ := vehicleType.TemporalValues( @@ -422,18 +422,18 @@ func (m *solutionMoveStopsImpl) deltaTravelDurationValue() float64 { travelDuration := 0.0 for _, stopPosition := range m.stopPositions { - modelStop := stopPosition.stop().ModelStop() - if stopPosition.next().IsPlanned() { - travelDuration -= stopPosition.next().TravelDurationValue() + modelStop := stopPosition.Stop().ModelStop() + if stopPosition.Next().IsPlanned() { + travelDuration -= stopPosition.Next().TravelDurationValue() travelDuration += vehicleType.TravelDurationExpression().Value( vehicleType, modelStop, - stopPosition.next().ModelStop(), + stopPosition.Next().ModelStop(), ) } travelDuration += vehicleType.TravelDurationExpression().Value( vehicleType, - stopPosition.previous().ModelStop(), + stopPosition.Previous().ModelStop(), modelStop, ) } @@ -493,7 +493,7 @@ func newMoveStops( ) } - vehicle := stopPositions[0].previous().vehicle() + vehicle := stopPositions[0].Previous().vehicle() lastPlannedPreviousStop := stopPositions[0].Previous() @@ -518,65 +518,65 @@ func newMoveStops( ) } - if stopPosition.previous().IsPlanned() { - if stopPosition.previous().Position() < position { + if stopPosition.Previous().IsPlanned() { + if stopPosition.Previous().Position() < position { return nil, fmt.Errorf("previous stop %s of stop position %v is planned at position %v,"+ " which is before or at the last planned previous stop %s at position %v", - stopPosition.previous().ModelStop().ID(), + stopPosition.Previous().ModelStop().ID(), index, - stopPosition.previous().Position(), + stopPosition.Previous().Position(), lastPlannedPreviousStop.ModelStop().ID(), lastPlannedPreviousStop.Position(), ) } - position = stopPosition.previous().Position() + position = stopPosition.Previous().Position() - lastPlannedPreviousStop = stopPosition.previous() + lastPlannedPreviousStop = stopPosition.Previous() } - if stopPosition.next().IsPlanned() { - if stopPosition.next().Position() < position { + if stopPosition.Next().IsPlanned() { + if stopPosition.Next().Position() < position { return nil, fmt.Errorf("next stop %s of stop position %v is planned at position %v,"+ " which is before or at the last planned previous stop %s at position %v", - stopPosition.next().ModelStop().ID(), + stopPosition.Next().ModelStop().ID(), index, - stopPosition.next().Position(), + stopPosition.Next().Position(), lastPlannedPreviousStop.ModelStop().ID(), lastPlannedPreviousStop.Position(), ) } - position = stopPosition.next().Position() + position = stopPosition.Next().Position() } - if stopPosition.next().IsPlanned() && !stopPosition.previous().IsPlanned() { - if lastPlannedPreviousStop.Position() != stopPosition.next().Position()-1 { + if stopPosition.Next().IsPlanned() && !stopPosition.Previous().IsPlanned() { + if lastPlannedPreviousStop.Position() != stopPosition.Next().Position()-1 { return nil, fmt.Errorf("stop positions are not allowed, planned previous stop %v is not adjacent"+ " to the planned next stop %v of the next stop position", lastPlannedPreviousStop.ModelStop().ID(), - stopPosition.next().ModelStop().ID(), + stopPosition.Next().ModelStop().ID(), ) } } - if stopPosition.next().IsPlanned() && stopPosition.previous().IsPlanned() { - if stopPosition.next().Position() != stopPosition.previous().Position()+1 { + if stopPosition.Next().IsPlanned() && stopPosition.Previous().IsPlanned() { + if stopPosition.Next().Position() != stopPosition.Previous().Position()+1 { return nil, fmt.Errorf("stop positions are not allowed, planned previous stop %v is not adjacent"+ " to the planned next stop %v of stop position %v", - stopPosition.previous().ModelStop().ID(), - stopPosition.next().ModelStop().ID(), + stopPosition.Previous().ModelStop().ID(), + stopPosition.Next().ModelStop().ID(), index, ) } } - if !stopPosition.previous().IsPlanned() { - if stopPositions[index-1].Stop() != stopPosition.previous() { + if !stopPosition.Previous().IsPlanned() { + if stopPositions[index-1].Stop() != stopPosition.Previous() { return nil, fmt.Errorf("the previous stop %s of stop position %v"+ " must be the stop %s of the previous stop position %v if it is unplanned", - stopPosition.previous().ModelStop().ID(), + stopPosition.Previous().ModelStop().ID(), index, stopPositions[index-1].Stop().ModelStop().ID(), index-1, @@ -584,12 +584,12 @@ func newMoveStops( } } - if !stopPosition.next().IsPlanned() { - if stopPositions[index+1].Stop() != stopPosition.next() { + if !stopPosition.Next().IsPlanned() { + if stopPositions[index+1].Stop() != stopPosition.Next() { return nil, fmt.Errorf("the next stop %s of stop position %v"+ " must be the stop %s of the next stop position %v if it is unplanned", - stopPosition.next().ModelStop().ID(), + stopPosition.Next().ModelStop().ID(), index, stopPositions[index+1].Stop().ModelStop().ID(), index+1, @@ -597,23 +597,23 @@ func newMoveStops( } } - if stopPosition.previous().IsPlanned() && stopPosition.previous().vehicle().index != vehicle.index { + if stopPosition.Previous().IsPlanned() && stopPosition.Previous().vehicle().index != vehicle.index { return nil, fmt.Errorf( "planned previous stop %v of stop position %v vehicle mismatch: %v != %v", - stopPosition.previous().ModelStop().ID(), + stopPosition.Previous().ModelStop().ID(), index, - stopPosition.previous().vehicle().ModelVehicle().ID(), + stopPosition.Previous().vehicle().ModelVehicle().ID(), vehicle.ModelVehicle().ID(), ) } - if stopPosition.next().IsPlanned() && stopPosition.next().vehicle().index != vehicle.index { + if stopPosition.Next().IsPlanned() && stopPosition.Next().vehicle().index != vehicle.index { return nil, fmt.Errorf( "planned next stop %v of stop position %v vehicle mismatch: %v != %v", - stopPosition.next().ModelStop().ID(), + stopPosition.Next().ModelStop().ID(), index, - stopPosition.next().vehicle().ModelVehicle().ID(), + stopPosition.Next().vehicle().ModelVehicle().ID(), vehicle.ModelVehicle().ID(), ) } diff --git a/solution_move_stops_generator.go b/solution_move_stops_generator.go index fc0cefb..76e5bbb 100644 --- a/solution_move_stops_generator.go +++ b/solution_move_stops_generator.go @@ -21,7 +21,7 @@ import ( // ) { // } func SolutionMoveStopsGeneratorChannel( - vehicle solutionVehicleImpl, + vehicle SolutionVehicle, planUnit *solutionPlanStopsUnitImpl, quit <-chan struct{}, stops SolutionStops, @@ -64,7 +64,7 @@ func SolutionMoveStopsGeneratorChannelTest( preAllocatedMoveContainer *PreAllocatedMoveContainer, ) chan SolutionMoveStops { return SolutionMoveStopsGeneratorChannel( - vehicle.(solutionVehicleImpl), + vehicle, planUnit.(*solutionPlanStopsUnitImpl), quit, stops, @@ -82,7 +82,7 @@ func SolutionMoveStopsGeneratorTest( shouldStop func() bool, ) { SolutionMoveStopsGenerator( - vehicle.(solutionVehicleImpl), + vehicle, planUnit.(*solutionPlanStopsUnitImpl), yield, stops, @@ -94,7 +94,7 @@ func SolutionMoveStopsGeneratorTest( // SolutionMoveStopsGenerator generates all possible moves for a given vehicle and // plan unit. The function yield is called for each solutionMoveStopsImpl. func SolutionMoveStopsGenerator( - vehicle solutionVehicleImpl, + vehicle SolutionVehicle, planUnit *solutionPlanStopsUnitImpl, yield func(move SolutionMoveStops), stops SolutionStops, @@ -221,12 +221,12 @@ func generate( stopPositions[positionIdx-1].nextStopIndex = stopPositions[positionIdx].stopIndex } else { stopPositions[positionIdx-1].nextStopIndex = target[combination[positionIdx-1]].index - if mustBeNeighbours(model, stopPositions[positionIdx-1].stop(), stopPositions[positionIdx].stop()) { + if mustBeNeighbours(model, stopPositions[positionIdx-1].Stop(), stopPositions[positionIdx].Stop()) { break } } - if isNotAllowed(model, stopPositions[positionIdx-1].stop(), stopPositions[positionIdx-1].next()) { + if isNotAllowed(model, stopPositions[positionIdx-1].Stop(), stopPositions[positionIdx-1].Next()) { combination = combination[:positionIdx] if stopPositions[positionIdx-1].nextStopIndex != stopPositions[positionIdx].previousStopIndex { break @@ -235,7 +235,7 @@ func generate( } } - if isNotAllowed(model, stopPositions[positionIdx].previous(), stopPositions[positionIdx].stop()) { + if isNotAllowed(model, stopPositions[positionIdx].Previous(), stopPositions[positionIdx].Stop()) { combination = combination[:positionIdx] continue } diff --git a/solution_move_test.go b/solution_move_test.go index d8f000d..770bc68 100644 --- a/solution_move_test.go +++ b/solution_move_test.go @@ -101,7 +101,7 @@ func TestVehicleBestMoveSinglePlanUnit(t *testing.T) { solutionVehicle := solution.SolutionVehicle(model.Vehicle(0)) - if solutionVehicle == nil { + if solutionVehicle.IsZero() { t.Error("solutionVehicle is nil") } @@ -188,7 +188,7 @@ func TestVehicleBestMoveSequencePlanUnit(t *testing.T) { solutionVehicle := solution.SolutionVehicle(model.Vehicle(0)) - if solutionVehicle == nil { + if solutionVehicle.IsZero() { t.Error("solutionVehicle is nil") } diff --git a/solution_stop.go b/solution_stop.go index d77c033..414195f 100644 --- a/solution_stop.go +++ b/solution_stop.go @@ -279,11 +279,11 @@ func (v SolutionStop) Vehicle() SolutionVehicle { if v.solution.next[v.index] == v.solution.previous[v.index] { panic("cannot get route of unplanned visit") } - return v.solution.solutionVehicles[v.solution.inVehicle[v.index]] + return v.vehicle() } -func (v SolutionStop) vehicle() solutionVehicleImpl { - return solutionVehicleImpl{ +func (v SolutionStop) vehicle() SolutionVehicle { + return SolutionVehicle{ index: v.solution.inVehicle[v.index], solution: v.solution, } diff --git a/solution_stop_generator.go b/solution_stop_generator.go index 5e53305..d839189 100644 --- a/solution_stop_generator.go +++ b/solution_stop_generator.go @@ -58,9 +58,9 @@ func newSolutionStopGenerator( startAtFirst bool, endAtLast bool, ) *solutionStopGeneratorImpl { - nextStop := move.vehicle().first() + nextStop := move.Vehicle().First() if !startAtFirst { - nextStop = move.stopPositions[0].previous() + nextStop = move.stopPositions[0].Previous() } solutionStopGenerator := solutionGeneratorPool.Get().(*solutionStopGeneratorImpl) solutionStopGenerator.stopPositions = solutionStopGenerator.stopPositions[:0] @@ -102,9 +102,9 @@ func (s *solutionStopGeneratorImpl) next() (SolutionStop, bool) { returnStop := s.nextStop if s.startAtFirst { - if s.nextStop == s.stopPositions[s.activeStopPositionIndex].previous() { + if s.nextStop == s.stopPositions[s.activeStopPositionIndex].Previous() { s.startAtFirst = false - s.nextStop = s.stopPositions[s.activeStopPositionIndex].stop() + s.nextStop = s.stopPositions[s.activeStopPositionIndex].Stop() } else { s.nextStop = s.nextStop.Next() } @@ -112,18 +112,18 @@ func (s *solutionStopGeneratorImpl) next() (SolutionStop, bool) { } if s.activeStopPositionIndex < len(s.stopPositions) { - if s.nextStop == s.stopPositions[s.activeStopPositionIndex].stop() { - s.nextStop = s.stopPositions[s.activeStopPositionIndex].next() + if s.nextStop == s.stopPositions[s.activeStopPositionIndex].Stop() { + s.nextStop = s.stopPositions[s.activeStopPositionIndex].Next() s.activeStopPositionIndex++ return returnStop, true } - if s.nextStop == s.stopPositions[s.activeStopPositionIndex].previous() { - s.nextStop = s.stopPositions[s.activeStopPositionIndex].stop() + if s.nextStop == s.stopPositions[s.activeStopPositionIndex].Previous() { + s.nextStop = s.stopPositions[s.activeStopPositionIndex].Stop() s.activeStopPositionIndex++ return returnStop, true } if !s.nextStop.IsPlanned() { - s.nextStop = s.stopPositions[s.activeStopPositionIndex-1].next() + s.nextStop = s.stopPositions[s.activeStopPositionIndex-1].Next() } else { if s.nextStop.IsLast() { s.endReached = true @@ -136,7 +136,7 @@ func (s *solutionStopGeneratorImpl) next() (SolutionStop, bool) { } if !s.nextStop.IsPlanned() { - s.nextStop = s.stopPositions[s.activeStopPositionIndex-1].next() + s.nextStop = s.stopPositions[s.activeStopPositionIndex-1].Next() return returnStop, true } diff --git a/solution_stop_position.go b/solution_stop_position.go index 6f12fb3..7393b82 100644 --- a/solution_stop_position.go +++ b/solution_stop_position.go @@ -67,12 +67,12 @@ func newStopPosition( func (v StopPosition) String() string { return fmt.Sprintf("stopPosition{%s[%v]->%s[%v]->%s[%v]", - v.previous().ModelStop().ID(), - v.previous().Index(), - v.stop().ModelStop().ID(), - v.stop().Index(), - v.next().ModelStop().ID(), - v.next().Index(), + v.Previous().ModelStop().ID(), + v.Previous().Index(), + v.Stop().ModelStop().ID(), + v.Stop().Index(), + v.Next().ModelStop().ID(), + v.Next().Index(), ) } @@ -80,38 +80,26 @@ func (v StopPosition) String() string { // involving the stop position is executed. It's worth noting that // the previous stop may not have been planned yet. func (v StopPosition) Previous() SolutionStop { - return v.previous() -} - -// Next denotes the upcoming stop's next stop if the associated move -// involving the stop position is executed. It's worth noting that -// the next stop may not have been planned yet. -func (v StopPosition) Next() SolutionStop { - return v.next() -} - -// Stop returns the stop which is not yet part of the solution. This stop -// is not planned yet if the move where the invoking stop position belongs -// to, has not been executed yet. -func (v StopPosition) Stop() SolutionStop { - return v.stop() -} - -func (v StopPosition) previous() SolutionStop { return SolutionStop{ index: v.previousStopIndex, solution: v.solution, } } -func (v StopPosition) next() SolutionStop { +// Next denotes the upcoming stop's next stop if the associated move +// involving the stop position is executed. It's worth noting that +// the next stop may not have been planned yet. +func (v StopPosition) Next() SolutionStop { return SolutionStop{ index: v.nextStopIndex, solution: v.solution, } } -func (v StopPosition) stop() SolutionStop { +// Stop returns the stop which is not yet part of the solution. This stop +// is not planned yet if the move where the invoking stop position belongs +// to, has not been executed yet. +func (v StopPosition) Stop() SolutionStop { return SolutionStop{ index: v.stopIndex, solution: v.solution, diff --git a/solution_vehicle.go b/solution_vehicle.go index 9324be5..1647810 100644 --- a/solution_vehicle.go +++ b/solution_vehicle.go @@ -14,94 +14,25 @@ import ( ) // SolutionVehicle is a vehicle in a solution. -type SolutionVehicle interface { - // FirstMove creates a move that adds the given plan unit to the - // vehicle after the first solution stop of the vehicle. The move is - // first feasible move after the first solution stop based on the - // estimates of the constraint, this move is not necessarily executable. - FirstMove(SolutionPlanUnit) (SolutionMove, error) - - // BestMove returns the best move for the given solution plan unit on - // the invoking vehicle. The best move is the move that has the lowest - // score. If there are no moves available for the given solution plan - // unit, a move is returned which is not executable, SolutionMoveStops.IsExecutable. - BestMove(context.Context, SolutionPlanUnit) SolutionMove - - // Duration returns the duration of the vehicle. The duration is the - // time the vehicle is on the road. The duration is the time between - // the start time and the end time. - Duration() time.Duration - // DurationValue returns the duration value of the vehicle. The duration - // value is the value of the duration of the vehicle. The duration value - // is the value in model duration units. - DurationValue() float64 - - // End returns the end time of the vehicle. The end time is the time - // the vehicle ends at the end stop. - End() time.Time - // EndValue returns the end value of the vehicle. The end value is the - // value of the end of the last stop. The end value is the value in - // model duration units since the model epoch. - EndValue() float64 - - // First returns the first stop of the vehicle. The first stop is the - // start stop. - First() SolutionStop - - // Index returns the index of the vehicle in the solution. - Index() int - // IsEmpty returns true if the vehicle is empty, false otherwise. A - // vehicle is empty if it does not have any stops. The start and end - // stops are not considered. - IsEmpty() bool - - // Last returns the last stop of the vehicle. The last stop is the end - // stop. - Last() SolutionStop - - // ModelVehicle returns the modeled vehicle type of the vehicle. - ModelVehicle() ModelVehicle - - // NumberOfStops returns the number of stops in the vehicle. The start - // and end stops are not considered. - NumberOfStops() int - - // SolutionStops returns the stops in the vehicle. The start and end - // stops are included in the returned stops. - SolutionStops() SolutionStops - // Start returns the start time of the vehicle. The start time is - // the time the vehicle starts at the start stop, it has been set - // in the factory method of the vehicle Solution.NewVehicle. - Start() time.Time - // StartValue returns the start value of the vehicle. The start value - // is the value of the start of the first stop. The start value is - // the value in model duration units since the model epoch. - StartValue() float64 - - // Unplan removes all stops from the vehicle. The start and end stops - // are not removed. Fixed stops are not removed. - Unplan() (bool, error) +type SolutionVehicle struct { + solution *solutionImpl + index int } // SolutionVehicles is a slice of solution vehicles. type SolutionVehicles []SolutionVehicle -type solutionVehicleImpl struct { - solution *solutionImpl - index int -} - func toSolutionVehicle( solution Solution, index int, ) SolutionVehicle { - return solutionVehicleImpl{ + return SolutionVehicle{ index: index, solution: solution.(*solutionImpl), } } -func (v solutionVehicleImpl) firstMovePlanStopsUnit( +func (v SolutionVehicle) firstMovePlanStopsUnit( planUnit *solutionPlanStopsUnitImpl, preAllocatedMoveContainer *PreAllocatedMoveContainer, ) (SolutionMove, error) { @@ -133,7 +64,7 @@ func (v solutionVehicleImpl) firstMovePlanStopsUnit( return bestMove, nil } -func (v solutionVehicleImpl) firstMovePlanUnitsUnit( +func (v SolutionVehicle) firstMovePlanUnitsUnit( planUnit *solutionPlanUnitsUnitImpl, ) (SolutionMove, error) { if planUnit.ModelPlanUnitsUnit().PlanOneOf() { @@ -142,7 +73,7 @@ func (v solutionVehicleImpl) firstMovePlanUnitsUnit( return v.firstMovePlanAllUnit(planUnit) } -func (v solutionVehicleImpl) firstMovePlanOneOfUnit( +func (v SolutionVehicle) firstMovePlanOneOfUnit( planUnit *solutionPlanUnitsUnitImpl, ) (SolutionMove, error) { planUnits := common.Shuffle( @@ -161,7 +92,7 @@ func (v solutionVehicleImpl) firstMovePlanOneOfUnit( return NotExecutableMove, nil } -func (v solutionVehicleImpl) firstMovePlanAllUnit( +func (v SolutionVehicle) firstMovePlanAllUnit( planUnit *solutionPlanUnitsUnitImpl, ) (SolutionMove, error) { planUnits := common.Shuffle( @@ -235,7 +166,7 @@ func updateMoveInPlace(move SolutionMoveStops, moveContainer moveContainer) { move.(*solutionMoveStopsImpl).value = moveContainer.value } -func (v solutionVehicleImpl) bestMovePlanSingleStop( +func (v SolutionVehicle) bestMovePlanSingleStop( _ context.Context, planUnit *solutionPlanStopsUnitImpl, preAllocatedMoveContainer *PreAllocatedMoveContainer, @@ -248,7 +179,7 @@ func (v solutionVehicleImpl) bestMovePlanSingleStop( move.(*solutionMoveStopsImpl).stopPositions, StopPosition{}, ) - stop := v.first() + stop := v.First() movesPtr := moveContainerPool.Get().(*[]moveContainer) moves := *movesPtr @@ -345,7 +276,7 @@ func (v solutionVehicleImpl) bestMovePlanSingleStop( return move } -func (v solutionVehicleImpl) bestMoveSequence( +func (v SolutionVehicle) bestMoveSequence( _ context.Context, planUnit *solutionPlanStopsUnitImpl, sequence SolutionStops, @@ -379,7 +310,7 @@ func (v solutionVehicleImpl) bestMoveSequence( return bestMove } -func (v solutionVehicleImpl) bestMovePlanMultipleStops( +func (v SolutionVehicle) bestMovePlanMultipleStops( ctx context.Context, planUnit *solutionPlanStopsUnitImpl, preAllocatedMoveContainer *PreAllocatedMoveContainer, @@ -394,7 +325,7 @@ func (v solutionVehicleImpl) bestMovePlanMultipleStops( return bestMove } -func (v solutionVehicleImpl) bestMovePlanStopsUnit( +func (v SolutionVehicle) bestMovePlanStopsUnit( ctx context.Context, planUnit *solutionPlanStopsUnitImpl, preAllocatedMoveContainer *PreAllocatedMoveContainer, @@ -406,7 +337,7 @@ func (v solutionVehicleImpl) bestMovePlanStopsUnit( return v.bestMovePlanMultipleStops(ctx, planUnit, preAllocatedMoveContainer) } -func (v solutionVehicleImpl) bestMovePlanUnitsUnit( +func (v SolutionVehicle) bestMovePlanUnitsUnit( ctx context.Context, planUnit *solutionPlanUnitsUnitImpl, ) SolutionMove { @@ -416,7 +347,7 @@ func (v solutionVehicleImpl) bestMovePlanUnitsUnit( return v.bestMovePlanAllUnit(ctx, planUnit) } -func (v solutionVehicleImpl) bestMovePlanOneOfUnit( +func (v SolutionVehicle) bestMovePlanOneOfUnit( ctx context.Context, planUnit *solutionPlanUnitsUnitImpl, ) SolutionMove { @@ -439,7 +370,7 @@ func revertMoves(moves SolutionMoves) (bool, error) { return true, nil } -func (v solutionVehicleImpl) bestMovePlanAllUnit( +func (v SolutionVehicle) bestMovePlanAllUnit( ctx context.Context, planUnit *solutionPlanUnitsUnitImpl, ) SolutionMove { @@ -484,7 +415,11 @@ func (v solutionVehicleImpl) bestMovePlanAllUnit( return newSolutionMoveUnits(planUnit, moves) } -func (v solutionVehicleImpl) FirstMove( +// FirstMove creates a move that adds the given plan unit to the +// vehicle after the first solution stop of the vehicle. The move is +// first feasible move after the first solution stop based on the +// estimates of the constraint, this move is not necessarily executable. +func (v SolutionVehicle) FirstMove( planUnit SolutionPlanUnit, ) (SolutionMove, error) { switch planUnit.(type) { @@ -497,7 +432,11 @@ func (v solutionVehicleImpl) FirstMove( return NotExecutableMove, nil } -func (v solutionVehicleImpl) BestMove( +// BestMove returns the best move for the given solution plan unit on +// the invoking vehicle. The best move is the move that has the lowest +// score. If there are no moves available for the given solution plan +// unit, a move is returned which is not executable, SolutionMoveStops.IsExecutable. +func (v SolutionVehicle) BestMove( ctx context.Context, planUnit SolutionPlanUnit, ) SolutionMove { @@ -508,7 +447,7 @@ func (v solutionVehicleImpl) BestMove( return v.bestMove(ctx, planUnit, allocations) } -func (v solutionVehicleImpl) bestMove( +func (v SolutionVehicle) bestMove( ctx context.Context, planUnit SolutionPlanUnit, sharedMoveContainer *PreAllocatedMoveContainer, @@ -530,72 +469,86 @@ func (v solutionVehicleImpl) bestMove( } } -func (v solutionVehicleImpl) IsEmpty() bool { - return v.last().Position() == 1 +// IsEmpty returns true if the vehicle is empty, false otherwise. A +// vehicle is empty if it does not have any stops. The start and end +// stops are not considered. +func (v SolutionVehicle) IsEmpty() bool { + return v.Last().Position() == 1 } -func (v solutionVehicleImpl) NumberOfStops() int { - return v.last().Position() - 1 +// NumberOfStops returns the number of stops in the vehicle. The start +// and end stops are not considered. +func (v SolutionVehicle) NumberOfStops() int { + return v.Last().Position() - 1 } -func (v solutionVehicleImpl) Index() int { +// Index returns the index of the vehicle in the solution. +func (v SolutionVehicle) Index() int { return v.index } -func (v solutionVehicleImpl) First() SolutionStop { - return v.first() -} - -func (v solutionVehicleImpl) Last() SolutionStop { - return v.last() -} - -func (v solutionVehicleImpl) first() SolutionStop { +// First returns the first stop of the vehicle. The first stop is the +// start stop. +func (v SolutionVehicle) First() SolutionStop { return SolutionStop{ index: v.solution.first[v.index], solution: v.solution, } } -func (v solutionVehicleImpl) last() SolutionStop { +// Last returns the last stop of the vehicle. The last stop is the end +// stop. +func (v SolutionVehicle) Last() SolutionStop { return SolutionStop{ index: v.solution.last[v.index], solution: v.solution, } } -func (v solutionVehicleImpl) DurationValue() float64 { +// DurationValue returns the duration value of the vehicle. The duration +// value is the value of the duration of the vehicle. The duration value +// is the value in model duration units. +func (v SolutionVehicle) DurationValue() float64 { return v.EndValue() - v.StartValue() } -func (v solutionVehicleImpl) Duration() time.Duration { +// Duration returns the duration of the vehicle. The duration is the +// time the vehicle is on the road. The duration is the time between +// the start time and the end time. +func (v SolutionVehicle) Duration() time.Duration { return v.End().Sub(v.Start()) } -func (v solutionVehicleImpl) StartValue() float64 { - return v.first().StartValue() +// StartValue returns the start value of the vehicle. The start value +// is the value of the start of the first stop. The start value is +// the value in model duration units since the model epoch. +func (v SolutionVehicle) StartValue() float64 { + return v.First().StartValue() } -func (v solutionVehicleImpl) Start() time.Time { - return v.first().Start() +// Start returns the start time of the vehicle. The start time is +// the time the vehicle starts at the start stop, it has been set +// in the factory method of the vehicle Solution.NewVehicle. +func (v SolutionVehicle) Start() time.Time { + return v.First().Start() } -func (v solutionVehicleImpl) EndValue() float64 { - return v.last().EndValue() +// EndValue returns the end value of the vehicle. The end value is the +// value of the end of the last stop. The end value is the value in +// model duration units since the model epoch. +func (v SolutionVehicle) EndValue() float64 { + return v.Last().EndValue() } -func (v solutionVehicleImpl) End() time.Time { - return v.last().End() +// End returns the end time of the vehicle. The end time is the time +// the vehicle ends at the end stop. +func (v SolutionVehicle) End() time.Time { + return v.Last().End() } -func (v solutionVehicleImpl) Next() SolutionStop { - return SolutionStop{ - index: v.solution.model.NumberOfStops() + v.index*2 + 1, - solution: v.solution, - } -} - -func (v solutionVehicleImpl) SolutionStops() SolutionStops { +// SolutionStops returns the stops in the vehicle. The start and end +// stops are included in the returned stops. +func (v SolutionVehicle) SolutionStops() SolutionStops { solutionStops := make(SolutionStops, 0, v.NumberOfStops()+2) solutionStop := v.First() for !solutionStop.IsLast() { @@ -606,24 +559,16 @@ func (v solutionVehicleImpl) SolutionStops() SolutionStops { return solutionStops } -func (v solutionVehicleImpl) solutionStops() []SolutionStop { - solutionStops := make([]SolutionStop, 0, v.NumberOfStops()+2) - solutionStop := v.first() - for !solutionStop.IsLast() { - solutionStops = append(solutionStops, solutionStop) - solutionStop = solutionStop.Next() - } - solutionStops = append(solutionStops, solutionStop) - return solutionStops -} - -func (v solutionVehicleImpl) ModelVehicle() ModelVehicle { +// ModelVehicle returns the modeled vehicle type of the vehicle. +func (v SolutionVehicle) ModelVehicle() ModelVehicle { return v.solution.model.Vehicle(v.solution.vehicleIndices[v.index]) } -func (v solutionVehicleImpl) Unplan() (bool, error) { +// Unplan removes all stops from the vehicle. The start and end stops +// are not removed. Fixed stops are not removed. +func (v SolutionVehicle) Unplan() (bool, error) { // TODO notify observers - solutionStops := common.Filter(v.solutionStops(), func(solutionStop SolutionStop) bool { + solutionStops := common.Filter(v.SolutionStops(), func(solutionStop SolutionStop) bool { return !solutionStop.IsFixed() }) if len(solutionStops) == 0 { @@ -659,8 +604,8 @@ func (v solutionVehicleImpl) Unplan() (bool, error) { if constraint != nil { for i := len(stopPositions) - 1; i >= 0; i-- { stopPosition := stopPositions[i] - beforeStop := stopPosition.next() - stopPosition.stop().attach( + beforeStop := stopPosition.Next() + stopPosition.Stop().attach( beforeStop.PreviousIndex(), ) } @@ -681,3 +626,9 @@ func (v solutionVehicleImpl) Unplan() (bool, error) { return true, nil } + +// IsZero returns true if the solution vehicle is the zero value. +// In this case it is not safe to use the solution vehicle. +func (v SolutionVehicle) IsZero() bool { + return v.solution == nil && v.index == 0 +} diff --git a/solve_operator_unplan_vehicles.go b/solve_operator_unplan_vehicles.go index 9cf6e05..1566807 100644 --- a/solve_operator_unplan_vehicles.go +++ b/solve_operator_unplan_vehicles.go @@ -74,7 +74,7 @@ func (d *solveOperatorUnPlanVehiclesImpl) Execute( vehicles := common.Filter( workSolution.vehicles, - func(vehicle solutionVehicleImpl) bool { + func(vehicle SolutionVehicle) bool { return vehicle.NumberOfStops() > 0 }, ) @@ -82,7 +82,7 @@ func (d *solveOperatorUnPlanVehiclesImpl) Execute( if len(vehicles) == 0 { return nil } - weights := common.Map(vehicles, func(vehicle solutionVehicleImpl) float64 { + weights := common.Map(vehicles, func(vehicle SolutionVehicle) float64 { return 1.0 + float64(workSolution.Model().NumberOfStops()-vehicle.NumberOfStops()) }) alias, err := common.NewAlias(weights)