package gradientdescent_test

import (
	"github.com/amitkgupta/goodlearn/optimizer/gradientdescent"

	"errors"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("GradientDescent", func() {
	var goodGradient func([]float64) ([]float64, error)

	BeforeEach(func() {
		goodGradient = func(x []float64) ([]float64, error) {
			g := make([]float64, len(x))
			for i, xi := range x {
				g[i] = 2 * xi
			}
			return g, nil
		}
	})

	Context("When given an empty initial guess", func() {
		It("Returns an error", func() {
			_, err := gradientdescent.GradientDescent([]float64{}, 0.05, 0.0005, 100000, goodGradient)
			Ω(err).Should(HaveOccurred())
		})
	})

	Context("When the given gradient function returns an error", func() {
		It("Returns an error", func() {
			badGradient := func(x []float64) ([]float64, error) {
				return nil, errors.New("I'm bad")
			}
			_, err := gradientdescent.GradientDescent([]float64{0.3, -0.4}, 0.05, 0.0005, 100000, badGradient)
			Ω(err).Should(HaveOccurred())
		})
	})

	Context("When given reasonable inputs", func() {
		var estimatedArgMin []float64
		var err error

		BeforeEach(func() {
			estimatedArgMin, err = gradientdescent.GradientDescent([]float64{0.3, -0.4}, 0.05, 0.0005, 100000, goodGradient)
		})

		It("Does not return an error", func() {
			Ω(err).ShouldNot(HaveOccurred())
		})

		It("Returns a reasonable output (local arg-min of the function with given gradient)", func() {
			Ω(estimatedArgMin[0]).Should(BeNumerically("~", 0.0, 0.005))
			Ω(estimatedArgMin[1]).Should(BeNumerically("~", 0.0, 0.005))
		})
	})
})

package gradientdescent_test

--- 
+++ 
@@ -1 +1,59 @@
 package gradientdescent_test
+
+import (
+	"github.com/amitkgupta/goodlearn/optimizer/gradientdescent"
+
+	"errors"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+)
+
+var _ = Describe("GradientDescent", func() {
+	var goodGradient func([]float64) ([]float64, error)
+
+	BeforeEach(func() {
+		goodGradient = func(x []float64) ([]float64, error) {
+			g := make([]float64, len(x))
+			for i, xi := range x {
+				g[i] = 2 * xi
+			}
+			return g, nil
+		}
+	})
+
+	Context("When given an empty initial guess", func() {
+		It("Returns an error", func() {
+			_, err := gradientdescent.GradientDescent([]float64{}, 0.05, 0.0005, 100000, goodGradient)
+			Ω(err).Should(HaveOccurred())
+		})
+	})
+
+	Context("When the given gradient function returns an error", func() {
+		It("Returns an error", func() {
+			badGradient := func(x []float64) ([]float64, error) {
+				return nil, errors.New("I'm bad")
+			}
+			_, err := gradientdescent.GradientDescent([]float64{0.3, -0.4}, 0.05, 0.0005, 100000, badGradient)
+			Ω(err).Should(HaveOccurred())
+		})
+	})
+
+	Context("When given reasonable inputs", func() {
+		var estimatedArgMin []float64
+		var err error
+
+		BeforeEach(func() {
+			estimatedArgMin, err = gradientdescent.GradientDescent([]float64{0.3, -0.4}, 0.05, 0.0005, 100000, goodGradient)
+		})
+
+		It("Does not return an error", func() {
+			Ω(err).ShouldNot(HaveOccurred())
+		})
+
+		It("Returns a reasonable output (local arg-min of the function with given gradient)", func() {
+			Ω(estimatedArgMin[0]).Should(BeNumerically("~", 0.0, 0.005))
+			Ω(estimatedArgMin[1]).Should(BeNumerically("~", 0.0, 0.005))
+		})
+	})
+})
Add test for gradient descent
