Repository: vaskoz/dailycodingproblem-go (go)

Issue:
Day 371
Good morning! Here's your coding interview problem for today.

This problem was asked by Google.

You are given a series of arithmetic equations as a string, such as:
```
y = x + 1
5 = x + 3
10 = z + y + 2
```
The equations use addition only and are separated by newlines. Return a mapping of all variables to their values. If it's not possible, then return null. In this example, you should return:
```
{
  x: 2,
  y: 3,
  z: 5
}
```

Pull request description:
Day371
Resolves #750.

Patch:
diff --git a/README.md b/README.md
index 8aadd78..24aa085 100644
--- a/README.md
+++ b/README.md
@@ -365,5 +365,7 @@ problems from
 * [Day 367](https://github.com/vaskoz/dailycodingproblem-go/issues/740)
 * [Day 368](https://github.com/vaskoz/dailycodingproblem-go/issues/743)
 * [Day 369](https://github.com/vaskoz/dailycodingproblem-go/issues/745)
+* Day 370 - Never received an e-mail for this day.
+* [Day 371](https://github.com/vaskoz/dailycodingproblem-go/issues/750)
 * [Day 372](https://github.com/vaskoz/dailycodingproblem-go/issues/748)
 
diff --git a/day371/problem.go b/day371/problem.go
new file mode 100644
index 0000000..5dabb21
--- /dev/null
+++ b/day371/problem.go
@@ -0,0 +1,82 @@
+package day371
+
+import (
+	"errors"
+	"sort"
+	"strconv"
+	"strings"
+)
+
+var errNoAnswerPossible = errors.New("an answer doesn't exist")
+
+type eqData struct {
+	vars  map[string]int
+	total int
+}
+
+// SolveArithmeticEquations returns the answer to these equations
+// or an error if no answer exists.
+// Will panic on malformed input.
+func SolveArithmeticEquations(equations string) (map[string]int, error) {
+	eqs := strings.Split(strings.TrimSpace(equations), "\n")
+	eqDatas := make([]eqData, 0, len(eqs))
+
+	for _, eq := range eqs {
+		sides := strings.Split(strings.TrimSpace(eq), "=")
+		if len(sides) != 2 {
+			panic("only one equal sign per line/equation")
+		}
+
+		lhs, rhs := strings.TrimSpace(sides[0]), strings.TrimSpace(sides[1])
+		vars := make(map[string]int)
+		total := 0
+
+		for _, v := range strings.Split(lhs, "+") {
+			if i, err := strconv.Atoi(v); err == nil {
+				total -= i
+			} else {
+				vars[v]++
+			}
+		}
+
+		for _, v := range strings.Split(rhs, "+") {
+			v = strings.TrimSpace(v)
+			if i, err := strconv.Atoi(v); err == nil {
+				total += i
+			} else {
+				vars[v]--
+			}
+		}
+
+		eqDatas = append(eqDatas, eqData{vars, total})
+	}
+
+	return solveArithmeticEquations(eqDatas)
+}
+
+func solveArithmeticEquations(equations []eqData) (map[string]int, error) {
+	ans := make(map[string]int)
+
+	sort.Slice(equations, func(i, j int) bool {
+		return len(equations[i].vars) < len(equations[j].vars)
+	})
+
+	for _, eq := range equations {
+		for v, coef := range eq.vars {
+			if a, exists := ans[v]; exists {
+				eq.total -= a * coef
+				delete(eq.vars, v)
+			}
+		}
+
+		if len(eq.vars) != 1 {
+			return nil, errNoAnswerPossible
+		}
+
+		for k, v := range eq.vars {
+			ans[k] = eq.total / v
+		}
+	}
+
+	return ans, nil
+}


Test patch:
diff --git a/day371/problem_test.go b/day371/problem_test.go
new file mode 100644
index 0000000..e4980c6
--- /dev/null
+++ b/day371/problem_test.go
@@ -0,0 +1,69 @@
+package day371
+
+import (
+	"reflect"
+	"testing"
+)
+
+// nolint
+var testcases = []struct {
+	equations   string
+	expected    map[string]int
+	expectedErr error
+}{
+	{
+		`y = x + 1
+	5 = x + 3
+	10 = z + y + 2`,
+		map[string]int{
+			"x": 2,
+			"y": 3,
+			"z": 5,
+		},
+		nil,
+	},
+	{
+		`y = x + 1
+	10 = z + y + 2`,
+		nil,
+		errNoAnswerPossible,
+	},
+	{
+		`
+		foo+1+foo+1+foo = foo + 12
+		 1 = bar+foo+2`,
+		map[string]int{
+			"foo": 5,
+			"bar": -6,
+		},
+		nil,
+	},
+}
+
+func TestSolveArithmeticEquations(t *testing.T) {
+	for _, tc := range testcases {
+		if result, err := SolveArithmeticEquations(tc.equations); !reflect.DeepEqual(result, tc.expected) ||
+			err != tc.expectedErr {
+			t.Errorf("Expected (%v,%v), got (%v,%v)", tc.expected, tc.expectedErr, result, err)
+		}
+	}
+}
+
+func TestSolveArithmeticEquationsBadInput(t *testing.T) {
+	defer func() {
+		if err := recover(); err == nil {
+			t.Errorf("Expected a panic when bad input")
+		}
+	}()
+	// nolint
+	SolveArithmeticEquations(`a+y=1 x+z=2
+x=5`)
+}
+
+func BenchmarkSolveArithmeticEquations(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		for _, tc := range testcases {
+			SolveArithmeticEquations(tc.equations) // nolint
+		}
+	}
+}