diff --git a/constants/constants.go b/constants/constants.go new file mode 100644 index 0000000..be5f304 --- /dev/null +++ b/constants/constants.go @@ -0,0 +1,9 @@ +package constants + +const ( + PARTNERS_FILE = "partners.csv" + CAPACITY_FILE = "capacities.csv" + INPUT_FILE = "input.csv" + OUTPUT1_FILE = "output1.csv" + OUTPUT2_FILE = "output2.csv" +) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..cca6ba0 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module qube + +go 1.16 diff --git a/main.go b/main.go new file mode 100644 index 0000000..b94b07e --- /dev/null +++ b/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "fmt" + "qube/service" +) + +func main() { + partners := service.ReadPartners() + + delivery := service.ReadDelivery() + + result, err := service.ProblemStatement1(partners, delivery) + + if err != nil { + fmt.Println(err.Error()) + return + } + + service.PrintProblemStatement1(result) + + capacities := service.ReadCapacity() + + result2, err := service.ProblemStatement2(partners, delivery, capacities) + + if err != nil { + fmt.Println(err.Error()) + return + } + + service.PrintProblemStatement2(result2) +} diff --git a/model/model.go b/model/model.go new file mode 100644 index 0000000..cf4fc8d --- /dev/null +++ b/model/model.go @@ -0,0 +1,28 @@ +package model + +type Partner struct { + MinGB int + MaxGB int + MinCost int + PerGB int + Partner string +} + +type Delivery struct { + Name string + Theatre string + Amount int +} + +type Result struct { + Name string + Partner string + Cost int +} + +type Result2 struct { + Name string + Amount int + Partner string + Cost int +} diff --git a/output2.csv b/output2.csv index adcd15b..d04c342 100644 --- a/output2.csv +++ b/output2.csv @@ -1,4 +1,4 @@ -D1,true ,P2,3000 -D2,true ,P1,3250 +D1,true ,P1,2000 +D2,true ,P2,3500 D3,true ,P3,15300 D4,false,"","" diff --git a/service/input.go b/service/input.go new file mode 100644 index 0000000..2eb08a8 --- /dev/null +++ b/service/input.go @@ -0,0 +1,174 @@ +package service + +import ( + "bufio" + "os" + "qube/constants" + "qube/model" + "strconv" + "strings" + "unicode" +) + +func ReadCapacity() map[string]int { + var capacities map[string]int = make(map[string]int) + readFile, err := os.Open(constants.CAPACITY_FILE) + + if err != nil { + panic(err) + } + + defer readFile.Close() + + fileScanner := bufio.NewScanner(readFile) + + fileScanner.Split(bufio.ScanLines) + + for fileScanner.Scan() { + data := strings.Split(SpaceStringsBuilder(fileScanner.Text()), ",") + + amount, err := parseInt(data[1]) + + if err != nil { + continue + } + + capacities[data[0]] = amount + } + + return capacities +} + +func ReadDelivery() []model.Delivery { + var delivery []model.Delivery + readFile, err := os.Open(constants.INPUT_FILE) + + if err != nil { + panic(err) + } + + defer readFile.Close() + + fileScanner := bufio.NewScanner(readFile) + + fileScanner.Split(bufio.ScanLines) + + for fileScanner.Scan() { + data := strings.Split(SpaceStringsBuilder(fileScanner.Text()), ",") + + newDelivery, err := parseDelivery(data) + + if err != nil { + continue + } + + delivery = append(delivery, *newDelivery) + } + + return delivery +} + +func ReadPartners() map[string][]model.Partner { + var partners map[string][]model.Partner = make(map[string][]model.Partner) + + readFile, err := os.Open(constants.PARTNERS_FILE) + + if err != nil { + panic(err) + } + + defer readFile.Close() + + fileScanner := bufio.NewScanner(readFile) + + fileScanner.Split(bufio.ScanLines) + + for fileScanner.Scan() { + data := strings.Split(SpaceStringsBuilder(fileScanner.Text()), ",") + + _, ok := partners[data[0]] + + if !ok { + partners[data[0]] = make([]model.Partner, 0) + } + + newPartner, err := parsePartner(data) + + if err != nil { + continue + } + + partners[data[0]] = append(partners[data[0]], *newPartner) + } + + return partners +} + +func parseDelivery(data []string) (*model.Delivery, error) { + amount, err := parseInt(data[1]) + + if err != nil { + return nil, err + } + + return &model.Delivery{ + Name: data[0], + Amount: amount, + Theatre: data[2], + }, nil +} + +func parsePartner(data []string) (*model.Partner, error) { + minCost, err := parseInt(data[2]) + + if err != nil { + return nil, err + } + + perGB, err := parseInt(data[3]) + + if err != nil { + return nil, err + } + + minGB, err := parseInt(strings.Split(data[1], "-")[0]) + + if err != nil { + return nil, err + } + + maxGB, err := parseInt(strings.Split(data[1], "-")[1]) + + if err != nil { + return nil, err + } + + return &model.Partner{ + MinCost: minCost, + PerGB: perGB, + Partner: data[4], + MinGB: minGB, + MaxGB: maxGB, + }, nil +} + +func parseInt(number string) (int, error) { + num, err := strconv.Atoi(number) + + if err != nil { + return 0, err + } + + return num, nil +} + +func SpaceStringsBuilder(str string) string { + var b strings.Builder + b.Grow(len(str)) + for _, ch := range str { + if !unicode.IsSpace(ch) { + b.WriteRune(ch) + } + } + return b.String() +} diff --git a/service/print.go b/service/print.go new file mode 100644 index 0000000..41f46d6 --- /dev/null +++ b/service/print.go @@ -0,0 +1,53 @@ +package service + +import ( + "bufio" + "fmt" + "os" + "qube/constants" + "qube/model" +) + +func PrintProblemStatement1(result []model.Result) { + f, err := os.Create(constants.OUTPUT1_FILE) + + if err != nil { + panic(err) + } + + defer f.Close() + + w := bufio.NewWriter(f) + + for _, elem := range result { + if elem.Cost == -1 { + fmt.Fprintf(w, "%v,false,\"\",\"\"\n", elem.Name) + } else { + fmt.Fprintf(w, "%v,true ,%v,%v\n", elem.Name, elem.Partner, elem.Cost) + } + } + + w.Flush() +} + +func PrintProblemStatement2(result []model.Result2) { + f, err := os.Create(constants.OUTPUT2_FILE) + + if err != nil { + panic(err) + } + + defer f.Close() + + w := bufio.NewWriter(f) + + for _, elem := range result { + if elem.Cost == -1 { + fmt.Fprintf(w, "%v,false,\"\",\"\"\n", elem.Name) + } else { + fmt.Fprintf(w, "%v,true ,%v,%v\n", elem.Name, elem.Partner, elem.Cost) + } + } + + w.Flush() +} diff --git a/service/service.go b/service/service.go new file mode 100644 index 0000000..f0dce1d --- /dev/null +++ b/service/service.go @@ -0,0 +1,173 @@ +package service + +import ( + "qube/model" + "sort" +) + +func ProblemStatement1(partners map[string][]model.Partner, deliveries []model.Delivery) ([]model.Result, error) { + var result []model.Result = make([]model.Result, 0, len(deliveries)) + + for _, delivery := range deliveries { + partnersForTheatre, ok := partners[delivery.Theatre] + + if !ok { + result = append(result, model.Result{ + Name: delivery.Name, + Cost: -1, + }) + continue + } + + var minCost int = -1 + var tempPartner string = "" + + for _, partner := range partnersForTheatre { + if partner.MinGB > delivery.Amount || delivery.Amount > partner.MaxGB { + continue + } + + cost := delivery.Amount * partner.PerGB + + if cost < partner.MinCost { + cost = partner.MinCost + } + + if cost < minCost || minCost == -1 { + minCost = cost + tempPartner = partner.Partner + } + + } + + result = append(result, model.Result{ + Name: delivery.Name, + Partner: tempPartner, + Cost: minCost, + }) + + } + + return result, nil +} + +func ProblemStatement2(partners map[string][]model.Partner, deliveries []model.Delivery, capacities map[string]int) ([]model.Result2, error) { + var result []model.Result2 = make([]model.Result2, 0, len(deliveries)) + var allCombinations [][]model.Result2 = make([][]model.Result2, 0) + + for _, delivery := range deliveries { + partnersForTheatre, ok := partners[delivery.Theatre] + + if !ok { + result = append(result, model.Result2{ + Name: delivery.Name, + Cost: -1, + }) + continue + } + + var partnerCombinations []model.Result2 = make([]model.Result2, 0, len(deliveries)) + + for _, partner := range partnersForTheatre { + if partner.MinGB > delivery.Amount || delivery.Amount > partner.MaxGB { + continue + } + + cost := delivery.Amount * partner.PerGB + + if cost < partner.MinCost { + cost = partner.MinCost + } + + partnerCombinations = append(partnerCombinations, model.Result2{ + Name: delivery.Name, + Amount: delivery.Amount, + Partner: partner.Partner, + Cost: cost, + }) + + } + + if len(partnerCombinations) == 0 { + result = append(result, model.Result2{ + Name: delivery.Name, + Cost: -1, + }) + } else { + allCombinations = addToEach(allCombinations, partnerCombinations) + } + + } + + result = append(checkEach(allCombinations, capacities), result...) + + sort.Slice(result, func(i, j int) bool { + return result[i].Name < result[j].Name + }) + + return result, nil +} + +func checkEach(allCombinations [][]model.Result2, capacities map[string]int) []model.Result2 { + var cost int = 0 + var amount = 0 + var indexRes int = 0 + + for index, slice := range allCombinations { + newCost, newAmount := check(slice, capacities) + + if newAmount < amount { + continue + } + + if newAmount > amount { + amount = newAmount + cost = newCost + indexRes = index + } else if newCost < cost { + cost = newCost + indexRes = index + } + } + + return allCombinations[indexRes] +} + +func check(check []model.Result2, capacities map[string]int) (int, int) { + var amount int = 0 + var cost int = 0 + var takenCapacities map[string]int = make(map[string]int) + + for _, elem := range check { + if takenCapacities[elem.Partner]+elem.Amount <= capacities[elem.Partner] { + amount++ + cost += elem.Cost + takenCapacities[elem.Partner] += elem.Amount + } + } + + return cost, amount +} + +func addToEach(to [][]model.Result2, from []model.Result2) [][]model.Result2 { + if len(from) == 0 { + return to + } + + var result [][]model.Result2 = make([][]model.Result2, 0) + + if len(to) == 0 { + for _, elem := range from { + result = append(result, []model.Result2{elem}) + } + return result + } + + for _, array := range to { + for _, elem := range from { + result = append(result, append(array, elem)) + } + } + + return result +}