diff --git a/README.md b/README.md
index b162885..a6e66b0 100644
--- a/README.md
+++ b/README.md
@@ -1,116 +1,81 @@
-# Qube Cinemas Challenge 2019
-Qube delivers the movie content to theatres all around the world. There are multiple delivery partners to help us deliver the content.
+# Qube Cinemas Challenge 2019 - Solution by Shreeyash Naik
-Delivery partners specify the rate of delivery and cost in following manner (All costs are in paise):
+## Solution for Problem Statement 1
-Table 1:
-
-| Theatre | Size Slab (in GB) | Minimum cost | Cost Per GB | Partner ID |
-| ------------- |:----------------: |:-------------:| :----------:|:----------:|
-| T1 | 0-200 | 2000 | 20 | P1 |
-| T1 | 200-400 | 3000 | 15 |P1 |
-| T3 | 100-200 | 4000 | 30 |P1 |
-| T3 | 200-400 | 5000 | 25 |P1 |
-| T5 | 100-200 | 2000 | 30 |P1 |
-| T1 | 0-400 | 1500 | 25 |P2 |
-
-First row allows 0 to 200 GB content to be sent to theatre T1 with the rate 20 paise per GB. However, if total cost comes less than minimum cost, minimum cost (2000 paise) will be charged.
-
-*NOTE*:
-- Multiple partners can deliver to same theatre
-
-
-- Write programs in any language you want. Feel free to hold the datasets in whatever data structure you want, but try not to use external databases - as far as possible stick to your langauage without bringing in MySQL/Postgres/MongoDB/Redis/Etc.
-
-- We've provided a CSV `partners.csv` with the list of all partners, theatres, content size, minimum cost and cost per GB. Please use the data mentioned there for this program instead of data given in Table 1 and 2. The codes you see in csv may be different from what you see in tables, so please always use the codes in the CSV. This Readme is only an example.
-
-This challenge consist of two problems. Aim to solve atleast Problem Statement 1.
-
-## Problem Statement 1
-Given a list of content size and Theatre ID, Find the partner for each delivery where cost of delivery is minimum. If delivery is not possible, mark that delivery impossible.
-
-Use the data given in `partners.csv`.
-
-
-**Input**: A CSV file `input.csv`. Each row containing delivery ID, size of delivery and theatre ID.
-
-**Expected Output**: A CSV `output.csv`. Each row containing delivery ID, indication if delivery is possible (true/false), selected partner and cost of delivery.
-
-#### Sample Scenarios (Based on above table 1):
-**INPUT**:
-```
-D1, 100, T1
-D2, 300, T1
-D3, 350, T1
-```
-**OUTPUT**:
-```
-D1, true, P1, 2000
-D2, true, P1, 4500
-D3, true, P1, 5250
-```
----
**INPUT**:
```
-D1, 70, T1
-D2, 300, T1
-```
-**OUTPUT**:
+D1,150,T1
+D2,325,T2
+D3,510,T1
+D4,700,T2
```
-D1, true, P2, 1750
-D2, true, P1, 4500
+**MY OUTPUT**:
```
-
----
-**INPUT**:
+D1,true ,P1,2000
+D2,true ,P1,3250
+D3,true ,P3,15300
+D4,false,,
```
-D1, 70, T3
-D2, 300, T1
-```
-**OUTPUT**:
-```
-D1, false, "", ""
-D2, true, P1, 4500
-```
-
-## Problem Statement 2
-Each partner specifies the **maximum capacity** they can serve, across all their deliveries in following manner:
+*ALGORITHM*:
+1. Stores details from partner.csv as a mapping of theatre_id -> all partner details.
+2. For theatre_id from a delivery_id, iterates over the partner_details and computes the minimum cost.
-Table 2:
-| Partner ID | Capacity (in GB) |
-| ------------- |:----------------:|
-| P1 | 500 |
-| P2 | 300 |
-
-We have provided `capacities.csv` which contain ID and capacities for each partner.
-
-Given a list of content size and Theatre ID, Assign deliveries to partners in such a way that all deliveries are possible (Higher Priority) and overall cost of delivery is minimum (i.e. First make sure no delivery is impossible and then minimise the sum of cost of all the delivery). If delivery is not possible to a theatre, mark that delivery impossible. Take partner capacity into consideration as well.
-
-Use `partners.csv` and `capacities.csv`.
-
-**Input**: Same as Problem statement 1.
+---
+
-**Expected Output**: Same as Problem statement 1.
+## Solution for Problem Statement 2
-#### Sample Scenario (Based on above table 1 and 2):
**INPUT**:
```
-D1, 100, T1
-D2, 240, T1
-D3, 260, T1
+D1,150,T1
+D2,325,T2
+D3,510,T1
+D4,700,T2
```
-**OUTPUT**:
+**MY OUTPUT**:
```
-D1, true, P2, 2500
-D2, true, P1, 3600
-D3, true, P1, 3900
+D1,true,P1,2000
+D2,true,P2,3500
+D3,true,P3,15300
+D4,false,,
```
+**NOTE**:
+For problem statement 2, my output is different than expected output. However, the solution satisfies all the conditions, plus overall cost of delivery (2000+3500+15300) is lesser than that of expected output.
+
+*ALGORITHM*:
+1. Stores details from partner.csv as a mapping of theatre_id -> all partner details.
+2. Stores in array, for each delivery_id in input.csv stores size_requirement, number of valid available Partner Options, details of each partner options.
+3. Sort the above array on the basis number of available options.
+4. Delivery with least number of options will get processed first.
+
+
+
+
+## Tree Layout
-**Explanation**: Only partner P1 and P2 can deliver content to T1. Lowest cost of delivery will be achieved if all three deliveries are given to partner P1 (100\*20+240\*15+260\*15 = 9,500). However, P1 has capacity of 500 GB and total assigned capacity is (100+240+260) 600 GB in this case. Assigning any one of the delivery to P2 will bring the capacity under 500. Assigning the D1, D2 and D3 to P2 is increasing the total cost of delivery by 500 (100\*25+240\*15+260\*15-9500), 2400 (100\*20+240\*25+260*15-9500) and 2600 (100\*20+240\*15+260\*25-9500) respectively. Hence, Assigning D1 to P2.
+```tree
+.
-To submit a solution, fork this repo and send a Pull Request on Github.
+├── go.mod
+├── main.go
+├── README.md
+├── common
+│ ├── db
+│ │ ├── db.go
+│ │ └── utils.go
+│ ├── schemas
+│ │ ├── schemas.go
+│ ├── utils
+│ │ ├── fileutils.go
+│ │ └── utils.go
+├── config
+│ └── config.go
+├── README.md
+└── src
+│ │── compute.go
+│ └── solutions.go
-For any questions or clarifications, raise an issue on this repo and we'll answer your questions as fast as we can.
+```
\ No newline at end of file
diff --git a/capacities.csv b/__data__/capacities.csv
similarity index 100%
rename from capacities.csv
rename to __data__/capacities.csv
diff --git a/input.csv b/__data__/input.csv
similarity index 100%
rename from input.csv
rename to __data__/input.csv
diff --git a/__data__/my_output1.csv b/__data__/my_output1.csv
new file mode 100644
index 0000000..1ae84bb
--- /dev/null
+++ b/__data__/my_output1.csv
@@ -0,0 +1,4 @@
+D1,true ,P1,2000
+D2,true ,P1,3250
+D3,true ,P3,15300
+D4,false,,
diff --git a/__data__/my_output2.csv b/__data__/my_output2.csv
new file mode 100644
index 0000000..43f2ee4
--- /dev/null
+++ b/__data__/my_output2.csv
@@ -0,0 +1,4 @@
+D1,true,P1,2000
+D2,true,P2,3500
+D3,true,P3,15300
+D4,false,,
diff --git a/__data__/output.csv b/__data__/output.csv
new file mode 100644
index 0000000..1ae84bb
--- /dev/null
+++ b/__data__/output.csv
@@ -0,0 +1,4 @@
+D1,true ,P1,2000
+D2,true ,P1,3250
+D3,true ,P3,15300
+D4,false,,
diff --git a/output1.csv b/__data__/output1.csv
similarity index 100%
rename from output1.csv
rename to __data__/output1.csv
diff --git a/output2.csv b/__data__/output2.csv
similarity index 100%
rename from output2.csv
rename to __data__/output2.csv
diff --git a/partners.csv b/__data__/partners.csv
similarity index 100%
rename from partners.csv
rename to __data__/partners.csv
diff --git a/common/db/db.go b/common/db/db.go
new file mode 100644
index 0000000..92247af
--- /dev/null
+++ b/common/db/db.go
@@ -0,0 +1,13 @@
+package db
+
+import (
+ "github.com/shreeyashnaik/challenge2019/common/schemas"
+)
+
+var (
+ // Stores details from partner.csv as a mapping of theatre_id -> all partner details
+ TheatrePartner map[string][]schemas.PartnerDetail
+
+ // Stores details of partner capacities as partner_id -> capacity (in GB)
+ Capacities map[string]int
+)
diff --git a/common/db/dbutils.go b/common/db/dbutils.go
new file mode 100644
index 0000000..e89cf8e
--- /dev/null
+++ b/common/db/dbutils.go
@@ -0,0 +1,57 @@
+package db
+
+import (
+ "log"
+
+ "github.com/shreeyashnaik/challenge2019/common/schemas"
+ "github.com/shreeyashnaik/challenge2019/common/utils"
+)
+
+func LoadPartnersCsv(path string) error {
+ TheatrePartner = make(map[string][]schemas.PartnerDetail)
+
+ partnerRows, err := utils.ReadCsv(path)
+ if err != nil {
+ return err
+ }
+
+ for num, row := range partnerRows {
+ if num == 0 {
+ continue
+ }
+ theatreID := utils.Trim(row[0])
+ partnerDetail := schemas.PartnerDetail{
+ PartnerID: utils.Trim(row[4]),
+ SizeSlab: utils.Trim(row[1]),
+ MinimumCost: utils.ToInt(row[2]),
+ CostPerGB: utils.ToInt(row[3]),
+ }
+
+ TheatrePartner[theatreID] = append(TheatrePartner[theatreID], partnerDetail)
+ }
+
+ log.Println(TheatrePartner)
+ return nil
+}
+
+func LoadCapacitiesCsv(path string) error {
+ Capacities = make(map[string]int)
+
+ capacities, err := utils.ReadCsv(path)
+ if err != nil {
+ return err
+ }
+
+ for num, row := range capacities {
+ if num == 0 {
+ continue
+ }
+ partnerID := utils.Trim(row[0])
+ capacity := utils.ToInt(row[1])
+
+ Capacities[partnerID] = capacity
+ }
+
+ log.Println(Capacities)
+ return nil
+}
diff --git a/common/schemas/schemas.go b/common/schemas/schemas.go
new file mode 100644
index 0000000..ea740f9
--- /dev/null
+++ b/common/schemas/schemas.go
@@ -0,0 +1,31 @@
+package schemas
+
+type PartnerCsvColumns struct {
+ TheatreID int
+ SizeSlab string
+ MinimumCost int
+ CostPerGB int
+ PartnerID int
+}
+
+// Details of each partner_id that is to be stored in DB for specific theatre_id
+type PartnerDetail struct {
+ PartnerID string
+ SizeSlab string
+ MinimumCost int
+ CostPerGB int
+}
+
+/**
+ Specific to Problem Statement 2
+ For each delivery_id in input.csv stores size_requirement, number of valid available Partner Options, details of each partner options.
+**/
+type DeliveryChoices struct {
+ DeliveryID string
+ SizeRequirement int
+ NumberOfOptions int
+ PartnerOptions []struct {
+ PartnerID string
+ CostOfDelivery int
+ }
+}
diff --git a/common/utils/fileutils.go b/common/utils/fileutils.go
new file mode 100644
index 0000000..6f810af
--- /dev/null
+++ b/common/utils/fileutils.go
@@ -0,0 +1,46 @@
+package utils
+
+import (
+ "bufio"
+ "encoding/csv"
+ "log"
+ "os"
+)
+
+// Reads CSV file from given filepath
+func ReadCsv(filepath string) ([][]string, error) {
+ csvFile, err := os.Open(filepath)
+ if err != nil {
+ log.Print("Unable to read csv file ", err)
+ return nil, err
+ }
+
+ defer csvFile.Close()
+
+ rows, err := csv.NewReader(csvFile).ReadAll()
+ if err != nil {
+ log.Print("Unable to parse csv file ", err)
+ return nil, err
+ }
+
+ return rows, nil
+}
+
+// Writes content to given filepath
+func WriteToCsv(filepath string, content []string) error {
+ outputCsv, err := os.Create(filepath)
+ if err != nil {
+ log.Print("Unable to create output file ", err)
+ return err
+ }
+
+ defer outputCsv.Close()
+
+ outputWriter := bufio.NewWriter(outputCsv)
+ defer outputWriter.Flush()
+ for _, line := range content {
+ outputWriter.WriteString(line + "\n")
+ }
+
+ return nil
+}
diff --git a/common/utils/utils.go b/common/utils/utils.go
new file mode 100644
index 0000000..0e059d6
--- /dev/null
+++ b/common/utils/utils.go
@@ -0,0 +1,23 @@
+package utils
+
+import (
+ "log"
+ "strconv"
+ "strings"
+)
+
+// Converts valid string int to int
+func ToInt(x string) int {
+ x = strings.Trim(x, " ")
+ val, err := strconv.ParseInt(x, 10, 64)
+ if err != nil {
+ log.Println("Unable to parse to int:", err)
+ }
+
+ return int(val)
+}
+
+// Trims all the spaces
+func Trim(x string) string {
+ return strings.Trim(x, " ")
+}
diff --git a/config/config.go b/config/config.go
new file mode 100644
index 0000000..1ab4d47
--- /dev/null
+++ b/config/config.go
@@ -0,0 +1,20 @@
+package config
+
+import (
+ "log"
+ "os"
+ "strings"
+)
+
+var (
+ SrcPath string
+)
+
+func LoadSrcPath() {
+ srcPath, err := os.Getwd()
+ if err != nil {
+ log.Println("Unable to fetch current path:", err)
+ }
+
+ SrcPath = strings.TrimRight(srcPath, "config")
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..ffce0b3
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module github.com/shreeyashnaik/challenge2019
+
+go 1.17
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..dbd82ec
--- /dev/null
+++ b/main.go
@@ -0,0 +1,35 @@
+package main
+
+import (
+ "log"
+
+ "github.com/shreeyashnaik/challenge2019/common/db"
+ "github.com/shreeyashnaik/challenge2019/config"
+ "github.com/shreeyashnaik/challenge2019/src"
+)
+
+func main() {
+ // Load current working directory to config.SrcPath
+ config.LoadSrcPath()
+ log.Println(config.SrcPath + "/__data__")
+
+ // Load partners.csv
+ if err := db.LoadPartnersCsv(config.SrcPath + "/__data__/partners.csv"); err != nil {
+ log.Println("Unable to load Partners csv to DB:", err)
+ }
+
+ // Load capacities.csv
+ if err := db.LoadCapacitiesCsv(config.SrcPath + "/__data__/capacities.csv"); err != nil {
+ log.Println("Unable to load Capacities csv to DB:", err)
+ }
+
+ // Problem Statement 1 solution
+ if err := src.ProblemStatementOne(config.SrcPath+"/__data__/input.csv", config.SrcPath+"/__data__/my_output1.csv"); err != nil {
+ log.Println("Error:", err)
+ }
+
+ // Problem Statement 2 solution
+ if err := src.ProblemStatementTwo(config.SrcPath+"/__data__/input.csv", config.SrcPath+"/__data__/my_output2.csv"); err != nil {
+ log.Println("Error:", err)
+ }
+}
diff --git a/src/compute.go b/src/compute.go
new file mode 100644
index 0000000..62609ba
--- /dev/null
+++ b/src/compute.go
@@ -0,0 +1,26 @@
+package src
+
+import (
+ "math"
+ "strings"
+
+ "github.com/shreeyashnaik/challenge2019/common/utils"
+)
+
+func ComputeCost(sizeSlab string, size, costPerGB, minimumCost int) int {
+ // Parse Upper & Lower bound from size slab
+ bound := strings.Split(sizeSlab, "-")
+
+ // If size is not between Size Slab
+ if size < utils.ToInt(bound[0]) || size > utils.ToInt(bound[1]) {
+ return math.MaxInt
+ }
+
+ // Compute cost
+ cost := size * costPerGB
+ if cost <= minimumCost {
+ return int(minimumCost)
+ }
+
+ return int(size * costPerGB)
+}
diff --git a/src/solutions.go b/src/solutions.go
new file mode 100644
index 0000000..931a4df
--- /dev/null
+++ b/src/solutions.go
@@ -0,0 +1,131 @@
+package src
+
+import (
+ "fmt"
+ "log"
+ "math"
+ "sort"
+
+ "github.com/shreeyashnaik/challenge2019/common/db"
+ "github.com/shreeyashnaik/challenge2019/common/schemas"
+ "github.com/shreeyashnaik/challenge2019/common/utils"
+)
+
+func ProblemStatementOne(inputPath, outputPath string) error {
+ inputRows, err := utils.ReadCsv(inputPath)
+ if err != nil {
+ return err
+ }
+
+ outputLines := []string{}
+ for _, row := range inputRows {
+ theatreID := utils.Trim(row[2])
+ size := utils.ToInt(row[1])
+
+ costOfDelivery := math.MaxInt
+ partnerID := "-1"
+ for _, partnerDetail := range db.TheatrePartner[theatreID] {
+ cost := ComputeCost(partnerDetail.SizeSlab, size, partnerDetail.CostPerGB, partnerDetail.MinimumCost)
+ if costOfDelivery > cost {
+ costOfDelivery = cost
+ partnerID = partnerDetail.PartnerID
+ }
+ log.Println(cost)
+ }
+
+ outputLine := ""
+ if partnerID != "-1" {
+ outputLine = fmt.Sprintf("%s,true ,%s,%d", utils.Trim(row[0]), partnerID, costOfDelivery)
+ } else {
+ outputLine = fmt.Sprintf("%s,false,,", utils.Trim(row[0]))
+ }
+
+ outputLines = append(outputLines, outputLine)
+ }
+
+ // Save solution to my_output1.go
+ if err := utils.WriteToCsv(outputPath, outputLines); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func ProblemStatementTwo(inputPath, outputPath string) error {
+ inputRows, err := utils.ReadCsv(inputPath)
+ if err != nil {
+ return err
+ }
+
+ // For each delivery_id in input.csv stores size_requirement, number of valid available Partner Options, details of each partner options.
+ deliveryChoices := []schemas.DeliveryChoices{}
+ for _, row := range inputRows {
+ deliveryID := utils.Trim(row[0])
+ size := utils.ToInt(row[1])
+ theatreID := utils.Trim(row[2])
+
+ choice := schemas.DeliveryChoices{
+ DeliveryID: deliveryID,
+ SizeRequirement: int(size),
+ }
+ numberOfOptions := 0
+ for _, partnerDetail := range db.TheatrePartner[theatreID] {
+ cost := ComputeCost(partnerDetail.SizeSlab, size, partnerDetail.CostPerGB, partnerDetail.MinimumCost)
+
+ if cost != math.MaxInt {
+ numberOfOptions += 1
+ choice.PartnerOptions = append(choice.PartnerOptions,
+ struct {
+ PartnerID string
+ CostOfDelivery int
+ }{
+ PartnerID: partnerDetail.PartnerID,
+ CostOfDelivery: cost,
+ },
+ )
+ }
+ log.Println(cost)
+ }
+
+ choice.NumberOfOptions = numberOfOptions
+ deliveryChoices = append(deliveryChoices, choice)
+ }
+
+ // Sort the above array on the basis number of available options.
+ sort.SliceStable(deliveryChoices, func(i, j int) bool {
+ return deliveryChoices[i].NumberOfOptions < deliveryChoices[j].NumberOfOptions
+ })
+
+ log.Println(deliveryChoices)
+
+ // Delivery with least number of options will get processed first.
+ output := make([]string, len(inputRows))
+ for _, delivery := range deliveryChoices {
+ finalCost := math.MaxInt
+ finalPartner := "-1"
+
+ for _, partnerChoice := range delivery.PartnerOptions {
+ if partnerChoice.CostOfDelivery < finalCost && delivery.SizeRequirement <= db.Capacities[partnerChoice.PartnerID] {
+ finalCost = partnerChoice.CostOfDelivery
+ finalPartner = partnerChoice.PartnerID
+
+ db.Capacities[partnerChoice.PartnerID] -= delivery.SizeRequirement
+ }
+ }
+
+ deliveryIdx := utils.ToInt(delivery.DeliveryID[1:]) - 1
+
+ if delivery.NumberOfOptions > 0 && finalPartner != "-1" {
+ output[deliveryIdx] = fmt.Sprintf("%s,true,%s,%d", delivery.DeliveryID, finalPartner, finalCost)
+ } else {
+ output[deliveryIdx] = fmt.Sprintf("%s,false,,", delivery.DeliveryID)
+ }
+ }
+
+ // Save solution to my_output2.go
+ if err := utils.WriteToCsv(outputPath, output); err != nil {
+ return err
+ }
+
+ return nil
+}