<commit_before>package clusters

import (
	"math"
	"sync"
)

type steepDownArea struct {
	start, end int
	mib        float64
}

type clusterJob struct {
	a, b, n int
}

type opticsClusterer struct {
	minpts, workers int
	eps, xi, x      float64

	distance DistanceFunc

	// slices holding the cluster mapping and sizes. Access is synchronized to avoid read during computation.
	mu   sync.RWMutex
	a, b []int

	// variables used for concurrent computation of nearest neighbours and producing final mapping
	l, s, o, f int
	j          chan *rangeJob
	c          chan *clusterJob
	m          *sync.Mutex
	w          *sync.WaitGroup
	p          *[]float64
	r          *[]int

	// visited points
	v []bool

	// reachability distances
	re []*pItem

	// ordered list of points wrt. reachability distance
	so []int

	// dataset
	d [][]float64
}

/* Implementation of OPTICS algorithm with concurrent nearest neighbour computation */
func OPTICS(minpts int, eps, xi float64, workers int, distance DistanceFunc) (HardClusterer, error) {
	if minpts < 1 {
		return nil, ErrZeroMinpts
	}

	if workers < 0 {
		return nil, ErrZeroWorkers
	}

	if eps <= 0 {
		return nil, ErrZeroEpsilon
	}

	if xi <= 0 {
		return nil, ErrZeroXi
	}

	var d DistanceFunc
	{
		if distance != nil {
			d = distance
		} else {
			d = EuclideanDistance
		}
	}

	return &opticsClusterer{
		minpts:   minpts,
		workers:  workers,
		eps:      eps,
		xi:       xi,
		x:        1 - xi,
		distance: d,
	}, nil
}

func (c *opticsClusterer) IsOnline() bool {
	return false
}

func (c *opticsClusterer) WithOnline(o Online) HardClusterer {
	return c
}

func (c *opticsClusterer) Learn(data [][]float64) error {
	if len(data) == 0 {
		return ErrEmptySet
	}

	c.mu.Lock()

	c.l = len(data)
	c.s = c.numWorkers()
	c.o = c.s - 1
	c.f = c.l / c.s

	c.d = data

	c.v = make([]bool, c.l)
	c.re = make([]*pItem, c.l)
	c.so = make([]int, 0, c.l)
	c.a = make([]int, c.l)
	c.b = make([]int, 0)

	c.startNearestWorkers()

	c.run()

	c.endNearestWorkers()

	c.v = nil
	c.p = nil
	c.r = nil

	c.startClusterWorkers()

	c.extract()

	c.endClusterWorkers()

	c.re = nil
	c.so = nil

	c.mu.Unlock()

	return nil
}

func (c *opticsClusterer) Sizes() []int {
	c.mu.RLock()
	defer c.mu.RUnlock()

	return c.b
}

func (c *opticsClusterer) Guesses() []int {
	c.mu.RLock()
	defer c.mu.RUnlock()

	return c.a
}

func (c *opticsClusterer) Predict(p []float64) int {
	var (
		l int
		d float64
		m float64 = c.distance(p, c.d[0])
	)

	for i := 1; i < len(c.d); i++ {
		if d = c.distance(p, c.d[i]); d < m {
			m = d
			l = i
		}
	}

	return c.a[l]
}

func (c *opticsClusterer) Online(observations chan []float64, done chan struct{}) chan *HCEvent {
	return nil
}

func (c *opticsClusterer) run() {
	var (
		l       int
		d       float64
		ns, nss []int = make([]int, 0), make([]int, 0)
		q       priorityQueue
		p       *pItem
	)

	for i := 0; i < c.l; i++ {
		if c.v[i] {
			continue
		}

		c.nearest(i, &l, &ns)

		c.v[i] = true

		c.so = append(c.so, i)

		if d = c.coreDistance(i, &l, &ns); d != 0 {
			q = newPriorityQueue(l)

			c.update(i, d, &l, &ns, &q)

			for q.NotEmpty() {
				p = q.Pop().(*pItem)

				c.nearest(p.v, &l, &nss)

				c.v[p.v] = true

				c.so = append(c.so, p.v)

				if d = c.coreDistance(p.v, &l, &nss); d != 0 {
					c.update(p.v, d, &l, &nss, &q)
				}
			}
		}
	}
}

func (c *opticsClusterer) coreDistance(p int, l *int, r *[]int) float64 {
	if *l < c.minpts {
		return 0
	}

	var d, m float64 = 0, c.distance(c.d[p], c.d[(*r)[0]])

	for i := 1; i < *l; i++ {
		if d = c.distance(c.d[p], c.d[(*r)[i]]); d > m {
			m = d
		}
	}

	return m
}

func (c *opticsClusterer) update(p int, d float64, l *int, r *[]int, q *priorityQueue) {
	for i := 0; i < *l; i++ {
		if !c.v[(*r)[i]] {
			m := math.Max(d, c.distance(c.d[p], c.d[(*r)[i]]))

			if c.re[(*r)[i]] == nil {
				item := &pItem{
					v: (*r)[i],
					p: m,
				}

				c.re[(*r)[i]] = item

				q.Push(item)
			} else if m < c.re[(*r)[i]].p {
				q.Update(c.re[(*r)[i]], c.re[(*r)[i]].v, m)
			}
		}
	}
}

func (c *opticsClusterer) extract() {
	var (
		i, k, e, ue, cs, ce, s, p int = 1, 1, 0, 0, 0, 0, 0, 0
		mib, d                    float64
		areas                     []*steepDownArea = make([]*steepDownArea, 0)
		clusters                  map[int]bool     = make(map[int]bool)
	)

	for i < c.l-2 {
		mib = math.Max(mib, c.re[c.so[i]].p)

		if c.isSteepDown(i, &e) {
			as := areas[:0]
			for j := 0; j < len(areas); j++ {
				if c.re[c.so[areas[j].start]].p*c.x < mib {
					continue
				}

				as = append(as, &steepDownArea{
					start: areas[j].start,
					end:   areas[j].end,
					mib:   math.Max(areas[j].mib, mib),
				})
			}
			areas = as

			areas = append(areas, &steepDownArea{
				start: i,
				end:   e,
			})

			i = e + 1
			mib = c.re[c.so[i]].p
		} else if c.isSteepUp(i, &e) {
			ue = e + 1

			as := areas[:0]
			for j := 0; j < len(areas); j++ {
				if c.re[c.so[areas[j].start]].p*c.x < mib {
					continue
				}

				as = append(as, &steepDownArea{
					start: areas[j].start,
					end:   areas[j].end,
					mib:   math.Max(areas[j].mib, mib),
				})
			}
			areas = as

			for j := 0; j < len(areas); j++ {
				if c.re[c.so[ue]].p*c.x < areas[j].mib {
					continue
				}

				d = (c.re[c.so[areas[j].start]].p - c.re[c.so[ue]].p) / c.re[c.so[areas[j].start]].p

				if math.Abs(d) <= c.xi {
					cs = areas[j].start
					ce = ue
				} else if d > c.xi {
					for k := areas[j].end; k > areas[j].end; k-- {
						if math.Abs((c.re[c.so[k]].p-c.re[c.so[ue]].p)/c.re[c.so[k]].p) <= c.xi {
							cs = k
							break
						}
					}
					ce = ue
				} else {
					cs = areas[j].start
					for k := i; k < e; k++ {
						if math.Abs((c.re[c.so[k]].p-c.re[c.so[i]].p)/c.re[c.so[k]].p) <= c.xi {
							ce = k
							break
						}
					}
				}

				p = ce - cs

				if p < c.minpts {
					continue
				}

				s = ce + cs

				if !clusters[s] {
					clusters[s] = true

					c.b = append(c.b, 0)
					c.b[k] = p

					c.w.Add(1)

					c.c <- &clusterJob{
						a: cs,
						b: ce,
						n: k,
					}

					k++
				}
			}

			i = ue
			mib = c.re[c.so[i]].p
		} else {
			i++
		}
	}

	c.w.Wait()
}

func (c *opticsClusterer) isSteepDown(i int, e *int) bool {
	if c.re[c.so[i]].p*c.x > c.re[c.so[i+1]].p {
		return false
	}

	var counter, j int = 0, i + 1

	*e = j

	for {
		if c.re[c.so[j+1]] == nil || c.re[c.so[j]].p < c.re[c.so[j+1]].p {
			break
		}

		if c.re[c.so[j]].p*c.x <= c.re[c.so[j+1]].p {
			*e = j
			counter = 0
		} else {
			counter++
		}

		if counter > c.minpts {
			break
		}

		j++
	}

	return *e != i+1
}

func (c *opticsClusterer) isSteepUp(i int, e *int) bool {
	if c.re[c.so[i]].p > c.re[c.so[i+1]].p*c.x {
		return false
	}

	var counter, j int = 0, i + 1

	*e = j

	for {
		if c.re[c.so[j+1]] == nil || c.re[c.so[j]].p > c.re[c.so[j+1]].p {
			break
		}

		if c.re[c.so[j]].p <= c.re[c.so[j+1]].p*c.x {
			*e = j
			counter = 0
		} else {
			counter++
		}

		if counter > c.minpts {
			break
		}

		j++
	}

	return *e != i+1
}

func (c *opticsClusterer) startClusterWorkers() {
	c.c = make(chan *clusterJob, c.l)

	c.w = &sync.WaitGroup{}

	for i := 0; i < c.s; i++ {
		go c.clusterWorker()
	}
}

func (c *opticsClusterer) endClusterWorkers() {
	close(c.c)

	c.c = nil

	c.w = nil
}

func (c *opticsClusterer) clusterWorker() {
	for j := range c.c {
		for i := j.a; i < j.b; i++ {
			c.a[i] = j.n
		}

		c.w.Done()
	}
}

/* Divide work among c.s workers, where c.s is determined
 * by the size of the data. This is based on an assumption that neighbour points of p
 * are located in relatively small subsection of the input data, so the dataset can be scanned
 * concurrently without blocking a big number of goroutines trying to write to r */
func (c *opticsClusterer) nearest(p int, l *int, r *[]int) {
	var b int

	*r = (*r)[:0]

	c.p = &c.d[p]
	c.r = r

	c.w.Add(c.s)

	for i := 0; i < c.l; i += c.f {
		if c.l-i <= c.f {
			b = c.l - 1
		} else {
			b = i + c.f
		}

		c.j <- &rangeJob{
			a: i,
			b: b,
		}
	}

	c.w.Wait()

	*l = len(*r)
}

func (c *opticsClusterer) startNearestWorkers() {
	c.j = make(chan *rangeJob, c.l)

	c.m = &sync.Mutex{}
	c.w = &sync.WaitGroup{}

	for i := 0; i < c.s; i++ {
		go c.nearestWorker()
	}
}

func (c *opticsClusterer) endNearestWorkers() {
	close(c.j)

	c.j = nil

	c.m = nil
	c.w = nil
}

func (c *opticsClusterer) nearestWorker() {
	for j := range c.j {
		for i := j.a; i < j.b; i++ {
			if c.distance(*c.p, c.d[i]) < c.eps {
				c.m.Lock()
				*c.r = append(*c.r, i)
				c.m.Unlock()
			}
		}

		c.w.Done()
	}
}

func (c *opticsClusterer) numWorkers() int {
	var b int

	if c.l < 1000 {
		b = 1
	} else if c.l < 10000 {
		b = 10
	} else if c.l < 100000 {
		b = 100
	} else {
		b = 1000
	}

	if c.workers == 0 {
		return b
	}

	if c.workers < b {
		return c.workers
	}

	return b
}
<commit_msg>Excluded first and last data points from cluster extraction in OPTICS<commit_after>package clusters

import (
	"math"
	"sync"
)

type steepDownArea struct {
	start, end int
	mib        float64
}

type clusterJob struct {
	a, b, n int
}

type opticsClusterer struct {
	minpts, workers int
	eps, xi, x      float64

	distance DistanceFunc

	// slices holding the cluster mapping and sizes. Access is synchronized to avoid read during computation.
	mu   sync.RWMutex
	a, b []int

	// variables used for concurrent computation of nearest neighbours and producing final mapping
	l, s, o, f, g int
	j             chan *rangeJob
	c             chan *clusterJob
	m             *sync.Mutex
	w             *sync.WaitGroup
	p             *[]float64
	r             *[]int

	// visited points
	v []bool

	// reachability distances
	re []*pItem

	// ordered list of points wrt. reachability distance
	so []int

	// dataset
	d [][]float64
}

/* Implementation of OPTICS algorithm with concurrent nearest neighbour computation */
func OPTICS(minpts int, eps, xi float64, workers int, distance DistanceFunc) (HardClusterer, error) {
	if minpts < 1 {
		return nil, ErrZeroMinpts
	}

	if workers < 0 {
		return nil, ErrZeroWorkers
	}

	if eps <= 0 {
		return nil, ErrZeroEpsilon
	}

	if xi <= 0 {
		return nil, ErrZeroXi
	}

	var d DistanceFunc
	{
		if distance != nil {
			d = distance
		} else {
			d = EuclideanDistance
		}
	}

	return &opticsClusterer{
		minpts:   minpts,
		workers:  workers,
		eps:      eps,
		xi:       xi,
		x:        1 - xi,
		distance: d,
	}, nil
}

func (c *opticsClusterer) IsOnline() bool {
	return false
}

func (c *opticsClusterer) WithOnline(o Online) HardClusterer {
	return c
}

func (c *opticsClusterer) Learn(data [][]float64) error {
	if len(data) == 0 {
		return ErrEmptySet
	}

	c.mu.Lock()

	c.l = len(data)
	c.s = c.numWorkers()
	c.o = c.s - 1
	c.f = c.l / c.s

	c.d = data

	c.v = make([]bool, c.l)
	c.re = make([]*pItem, c.l)
	c.so = make([]int, 0, c.l)
	c.a = make([]int, c.l)
	c.b = make([]int, 0)

	c.startNearestWorkers()

	c.run()

	c.endNearestWorkers()

	c.v = nil
	c.p = nil
	c.r = nil

	c.startClusterWorkers()

	c.extract()

	c.endClusterWorkers()

	c.re = nil
	c.so = nil

	c.mu.Unlock()

	return nil
}

func (c *opticsClusterer) Sizes() []int {
	c.mu.RLock()
	defer c.mu.RUnlock()

	return c.b
}

func (c *opticsClusterer) Guesses() []int {
	c.mu.RLock()
	defer c.mu.RUnlock()

	return c.a
}

func (c *opticsClusterer) Predict(p []float64) int {
	var (
		l int
		d float64
		m float64 = c.distance(p, c.d[0])
	)

	for i := 1; i < len(c.d); i++ {
		if d = c.distance(p, c.d[i]); d < m {
			m = d
			l = i
		}
	}

	return c.a[l]
}

func (c *opticsClusterer) Online(observations chan []float64, done chan struct{}) chan *HCEvent {
	return nil
}

func (c *opticsClusterer) run() {
	var (
		l       int
		d       float64
		ns, nss []int = make([]int, 0), make([]int, 0)
		q       priorityQueue
		p       *pItem
	)

	for i := 0; i < c.l; i++ {
		if c.v[i] {
			continue
		}

		c.nearest(i, &l, &ns)

		c.v[i] = true

		c.so = append(c.so, i)

		if d = c.coreDistance(i, &l, &ns); d != 0 {
			q = newPriorityQueue(l)

			c.update(i, d, &l, &ns, &q)

			for q.NotEmpty() {
				p = q.Pop().(*pItem)

				c.nearest(p.v, &l, &nss)

				c.v[p.v] = true

				c.so = append(c.so, p.v)

				if d = c.coreDistance(p.v, &l, &nss); d != 0 {
					c.update(p.v, d, &l, &nss, &q)
				}
			}
		}
	}
}

func (c *opticsClusterer) coreDistance(p int, l *int, r *[]int) float64 {
	if *l < c.minpts {
		return 0
	}

	var d, m float64 = 0, c.distance(c.d[p], c.d[(*r)[0]])

	for i := 1; i < *l; i++ {
		if d = c.distance(c.d[p], c.d[(*r)[i]]); d > m {
			m = d
		}
	}

	return m
}

func (c *opticsClusterer) update(p int, d float64, l *int, r *[]int, q *priorityQueue) {
	for i := 0; i < *l; i++ {
		if !c.v[(*r)[i]] {
			m := math.Max(d, c.distance(c.d[p], c.d[(*r)[i]]))

			if c.re[(*r)[i]] == nil {
				item := &pItem{
					v: (*r)[i],
					p: m,
				}

				c.re[(*r)[i]] = item

				q.Push(item)
			} else if m < c.re[(*r)[i]].p {
				q.Update(c.re[(*r)[i]], c.re[(*r)[i]].v, m)
			}
		}
	}
}

func (c *opticsClusterer) extract() {
	var (
		i, k, e, ue, cs, ce, s, p int = 0, 1, 0, 0, 0, 0, 0, 0
		mib, d                    float64
		areas                     []*steepDownArea = make([]*steepDownArea, 0)
		clusters                  map[int]bool     = make(map[int]bool)
	)

	// first and last points are not assigned the reachability distance, so exclude them from calculations
	c.so = c.so[1 : len(c.so)-2]
	c.g = len(c.so) - 2

	for i < len(c.so)-1 {
		mib = math.Max(mib, c.re[c.so[i]].p)

		if c.isSteepDown(i, &e) {
			as := areas[:0]
			for j := 0; j < len(areas); j++ {
				if c.re[c.so[areas[j].start]].p*c.x < mib {
					continue
				}

				as = append(as, &steepDownArea{
					start: areas[j].start,
					end:   areas[j].end,
					mib:   math.Max(areas[j].mib, mib),
				})
			}
			areas = as

			areas = append(areas, &steepDownArea{
				start: i,
				end:   e,
			})

			i = e + 1
			mib = c.re[c.so[i]].p
		} else if c.isSteepUp(i, &e) {
			ue = e + 1

			as := areas[:0]
			for j := 0; j < len(areas); j++ {
				if c.re[c.so[areas[j].start]].p*c.x < mib {
					continue
				}

				as = append(as, &steepDownArea{
					start: areas[j].start,
					end:   areas[j].end,
					mib:   math.Max(areas[j].mib, mib),
				})
			}
			areas = as

			for j := 0; j < len(areas); j++ {
				if c.re[c.so[ue]].p*c.x < areas[j].mib {
					continue
				}

				d = (c.re[c.so[areas[j].start]].p - c.re[c.so[ue]].p) / c.re[c.so[areas[j].start]].p

				if math.Abs(d) <= c.xi {
					cs = areas[j].start
					ce = ue
				} else if d > c.xi {
					for k := areas[j].end; k > areas[j].end; k-- {
						if math.Abs((c.re[c.so[k]].p-c.re[c.so[ue]].p)/c.re[c.so[k]].p) <= c.xi {
							cs = k
							break
						}
					}
					ce = ue
				} else {
					cs = areas[j].start
					for k := i; k < e; k++ {
						if math.Abs((c.re[c.so[k]].p-c.re[c.so[i]].p)/c.re[c.so[k]].p) <= c.xi {
							ce = k
							break
						}
					}
				}

				p = ce - cs

				if p < c.minpts {
					continue
				}

				s = ce + cs

				if !clusters[s] {
					clusters[s] = true

					c.b = append(c.b, 0)
					c.b[k] = p

					c.w.Add(1)

					c.c <- &clusterJob{
						a: cs,
						b: ce,
						n: k,
					}

					k++
				}
			}

			i = ue
			mib = c.re[c.so[i]].p
		} else {
			i++
		}
	}

	c.w.Wait()
}

func (c *opticsClusterer) isSteepDown(i int, e *int) bool {
	if c.re[c.so[i]].p*c.x > c.re[c.so[i+1]].p {
		return false
	}

	var counter, j int = 0, i

	*e = j

	for {
		if j > c.g {
			break
		}

		if c.re[c.so[j]].p < c.re[c.so[j+1]].p {
			break
		}

		if c.re[c.so[j]].p*c.x <= c.re[c.so[j+1]].p {
			*e = j
			counter = 0
		} else {
			counter++
		}

		if counter > c.minpts {
			break
		}

		j++
	}

	return *e != i
}

func (c *opticsClusterer) isSteepUp(i int, e *int) bool {
	if c.re[c.so[i]].p > c.re[c.so[i+1]].p*c.x {
		return false
	}

	var counter, j int = 0, i

	*e = j

	for {
		if j > c.g {
			break
		}

		if c.re[c.so[j]].p > c.re[c.so[j+1]].p {
			break
		}

		if c.re[c.so[j]].p <= c.re[c.so[j+1]].p*c.x {
			*e = j
			counter = 0
		} else {
			counter++
		}

		if counter > c.minpts {
			break
		}

		j++
	}

	return *e != i
}

func (c *opticsClusterer) startClusterWorkers() {
	c.c = make(chan *clusterJob, c.l)

	c.w = &sync.WaitGroup{}

	for i := 0; i < c.s; i++ {
		go c.clusterWorker()
	}
}

func (c *opticsClusterer) endClusterWorkers() {
	close(c.c)

	c.c = nil

	c.w = nil
}

func (c *opticsClusterer) clusterWorker() {
	for j := range c.c {
		for i := j.a; i < j.b; i++ {
			c.a[i] = j.n
		}

		c.w.Done()
	}
}

/* Divide work among c.s workers, where c.s is determined
 * by the size of the data. This is based on an assumption that neighbour points of p
 * are located in relatively small subsection of the input data, so the dataset can be scanned
 * concurrently without blocking a big number of goroutines trying to write to r */
func (c *opticsClusterer) nearest(p int, l *int, r *[]int) {
	var b int

	*r = (*r)[:0]

	c.p = &c.d[p]
	c.r = r

	c.w.Add(c.s)

	for i := 0; i < c.l; i += c.f {
		if c.l-i <= c.f {
			b = c.l - 1
		} else {
			b = i + c.f
		}

		c.j <- &rangeJob{
			a: i,
			b: b,
		}
	}

	c.w.Wait()

	*l = len(*r)
}

func (c *opticsClusterer) startNearestWorkers() {
	c.j = make(chan *rangeJob, c.l)

	c.m = &sync.Mutex{}
	c.w = &sync.WaitGroup{}

	for i := 0; i < c.s; i++ {
		go c.nearestWorker()
	}
}

func (c *opticsClusterer) endNearestWorkers() {
	close(c.j)

	c.j = nil

	c.m = nil
	c.w = nil
}

func (c *opticsClusterer) nearestWorker() {
	for j := range c.j {
		for i := j.a; i < j.b; i++ {
			if c.distance(*c.p, c.d[i]) < c.eps {
				c.m.Lock()
				*c.r = append(*c.r, i)
				c.m.Unlock()
			}
		}

		c.w.Done()
	}
}

func (c *opticsClusterer) numWorkers() int {
	var b int

	if c.l < 1000 {
		b = 1
	} else if c.l < 10000 {
		b = 10
	} else if c.l < 100000 {
		b = 100
	} else {
		b = 1000
	}

	if c.workers == 0 {
		return b
	}

	if c.workers < b {
		return c.workers
	}

	return b
}
<|endoftext|>


<commit_before>package goshopify

import (
	"errors"
	"fmt"
	"net/url"
)

type OrdersCount struct {
	Count int64 `json:"count"`
}

type OrderResponse struct {
	Order *Order `json:"order"`
}

type OrdersResponse struct {
	Orders []*Order `json:"orders"`
}

type Order struct {
	BuyerAcceptsMarketing bool             `json:"buyer_accepts_marketing"`
	CancelReason          string           `json:"cancel_reason"`
	CancelledAt           string           `json:"cancelled_at"`
	CartToken             string           `json:"cart_token"`
	CheckoutToken         string           `json:"checkout_token"`
	ClosedAt              string           `json:"closed_at"`
	Confirmed             bool             `json:"confirmed"`
	CreatedAt             string           `json:"created_at"`
	Currency              string           `json:"currency"`
	DevicdId              int64            `json:"device_id"`
	Email                 string           `json:"email"`
	FinancialStatus       string           `json:"financial_status"`
	FulfillmentStatus     string           `json:"fulfillment_status"`
	Gateway               string           `json:"gateway"`
	Id                    int64            `json:"id"`
	LandingSite           string           `json:"landing_site"`
	LocationId            int64            `json:"location_id"`
	Name                  string           `json:"name"`
	Note                  string           `json:"note"`
	Number                int64            `json:"number"`
	ProcessedAt           string           `json:"processed_at"`
	Reference             string           `json:"reference"`
	ReferringSite         string           `json:"referring_site"`
	SourceIdentifier      string           `json:"source_identifier"`
	SourceUrl             string           `json:"source_url"`
	SubtotalPrice         string           `json:"subtotal_price"`
	TaxesIncluded         bool             `json:"taxes_included"`
	Test                  bool             `json:"test"`
	Token                 string           `json:"token"`
	TotalDiscount         string           `json:"total_discounts"`
	TotalLineItemsPrice   string           `json:"total_line_items_price"`
	TotalPrice            string           `json:"total_price"`
	TotalPriceUsd         string           `json:"total_price_usd"`
	TotalTax              string           `json:"total_tax"`
	TotalWeight           int64            `json:"total_weight"`
	UpdatedAt             string           `json:"updated_at"`
	UserId                int64            `json:"user_id"`
	BrowserIp             string           `json:"browser_ip"`
	LandingSiteRef        string           `json:"landing_site_ref"`
	OrderNumber           int64            `json:"order_number"`
	DiscountCodes         []*DiscountCode  `json:"discount_codes"`
	NoteAttributes        []*NoteAttribute `json:"note_attributes"`
	PaymentGatewayNames   []*string        `json:"payment_gateway_names"`
	ProcessingMethod      string           `json:"processing_method"`
	Source                string           `json:"source"`
	CheckoutId            int64            `json:"checkout_id"`
	SourceName            string           `json:"source_name"`
	TaxLines              []*TaxLine       `json:"tax_lines"`
	Tags                  string           `json:"tags"`
	LineItems             []*LineItem      `json:"line_items"`
	ShippingLines         []*ShippingLine  `json:"shipping_lines"`
	BillingAddress        *Address         `json:"billing_address"`
	ShippingAddress       *Address         `json:"shipping_address"`
	Fulfillments          []*Fulfillment   `json:"fulfillments"`
	ClientDetails         *ClientDetails   `json:"client_details"`
	Refunds               []*Refund        `json:"refunds"`
	Customer              *Customer        `json:"customer"`
}

type DiscountCode struct {
	Code   string `json:"code"`
	Amount string `json:"amount"`
	Type   string `json:"type"`
}

type NoteAttribute struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

type TaxLine struct {
	Price string  `json:"price"`
	Rate  float64 `json:"rate"`
	Title string  `json:"title"`
}

type LineItem struct {
	FulfillmentService         string      `json:"fulfillment_service"`
	FulfillmentStatus          string      `json:"fulfillment_status"`
	GiftCard                   bool        `json:"gift_card"`
	Grams                      float64     `json:"grams"`
	Id                         int64       `json:"id"`
	Price                      string      `json:"price"`
	ProductId                  int64       `json:"product_id"`
	Quantity                   int64       `json:"quantity"`
	RequiresShipping           bool        `json:"requires_shipping"`
	Sku                        string      `json:"sku"`
	Taxable                    bool        `json:"taxable"`
	Title                      string      `json:"title"`
	VariantId                  int64       `json:"variant_id"`
	VariantTitle               string      `json:"variant_title"`
	Vendor                     string      `json:"vendor"`
	Name                       string      `json:"name"`
	VariantInventoryManagement string      `json:"variant_inventory_management"`
	Properties                 []*Property `json:"properties"`
	ProductExists              bool        `json:"product_exists"`
	FulfillableQuantity        int64       `json:"fulfillable_quantity"`
	TotalDiscount              string      `json:"total_discount"`
	TaxLines                   []*TaxLine  `json:"tax_lines"`
}

type Property struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

type ShippingLine struct {
	Code     string     `json:"code"`
	Price    string     `json:"price"`
	Source   string     `json:"source"`
	Title    string     `json:"title"`
	TaxLines []*TaxLine `json:"tax_lines"`
}

type Address struct {
	Address1     string  `json:"address1"`
	Address2     string  `json:"address2"`
	City         string  `json:"city"`
	Company      string  `json:"company"`
	Country      string  `json:"country"`
	FirstName    string  `json:"first_name"`
	LastName     string  `json:"last_name"`
	Latitude     float64 `json:"latitude"`
	Longitude    float64 `json:"longitude"`
	Phone        string  `json:"phone"`
	Province     string  `json:"province"`
	Zip          string  `json:"zip"`
	Name         string  `json:"name"`
	CountryCode  string  `json:"country_code"`
	ProvinceCode string  `json:"province_code"`
}

type ClientDetails struct {
	AcceptLanguage string `json:"accept_language"`
	BrowserHeight  int64  `json:"browser_height"`
	BrowserIp      string `json:"browser_ip"`
	BrowserWidth   int64  `json:"browser_width"`
	SessionHash    string `json:"session_hash"`
	UserAgent      string `json:"user_agent"`
}

type Refund struct {
	CreatedAt       string            `json:"created_at"`
	Id              int64             `json:"id"`
	Note            string            `json:"note"`
	OrderId         int64             `json:"order_id"`
	Restock         bool              `json:"restock"`
	UserId          int64             `json:"user_id"`
	RefundLineItems []*RefundLineItem `json:"refund_line_items"`
	Transactions    []*Transaction    `json:"transactions"`
}

type RefundLineItem struct {
	Id         int64     `json:"id"`
	LineItemId int64     `json:"line_item_id"`
	Quantity   int64     `json:"quantity"`
	LineItem   *LineItem `json:"line_item"`
}

func (s *Shopify) GetOrdersCount(creds *Credentials, params url.Values) (*OrdersCount, error) {
	uri, err := s.getUri(OrdersCountEndpoint, creds, params)
	if err != nil {
		return nil, err
	}

	var count *OrdersCount
	err = s.DoRequest("GET", uri, creds, nil, &count)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("OrdersCount: %s", err.Error()))
	}

	return count, nil
}

func (s *Shopify) GetOrders(creds *Credentials, params url.Values) ([]*Order, error) {
	uri, err := s.getUri(OrdersEndpoint, creds, params)
	if err != nil {
		return nil, err
	}

	var ordersResponse *OrdersResponse
	err = s.DoRequest("GET", uri, creds, nil, &ordersResponse)
	if err != nil {
		return nil, fmt.Errorf("Request to Shopify Orders failed: %s", err.Error())
	}

	return ordersResponse.Orders, nil
}

func (s *Shopify) GetOrdersSinceId(creds *Credentials, sinceId string, params url.Values) ([]*Order, error) {
	if params == nil {
		params = url.Values{}
	}
	params.Add("since_id", sinceId)
	return s.GetOrders(creds, params)
}

func (s *Shopify) GetOrdersSinceOrderNumber(creds *Credentials, sinceNum string, params url.Values) ([]*Order, error) {
	sinceId, err := s.FindOrderIdFromOrderNumber(creds, sinceNum)
	if err != nil {
		return nil, err
	}
	return s.GetOrdersSinceId(creds, sinceId, params)
}

func (s *Shopify) FindOrderIdFromOrderNumber(creds *Credentials, orderNum string) (string, error) {
	params := url.Values{}
	params.Add("name", orderNum)
	params.Add("status", "any")
	params.Add("fields", "id,order_number")
	uri, err := s.getUri(OrdersEndpoint, creds, params)
	var ordersResponse *OrdersResponse
	err = s.DoRequest("GET", uri, creds, nil, &ordersResponse)
	if err != nil {
		return "", fmt.Errorf("Request to Shopify Orders failed: %s", err.Error())
	}
	if ordersResponse == nil || ordersResponse.Orders == nil || len(ordersResponse.Orders) != 1 {
		return "", fmt.Errorf("invalid response from shopify when resolving orders")
	}
	return fmt.Sprintf("%v", ordersResponse.Orders[0].Id), nil
}

func (s *Shopify) FindOrderNumberFromOrderId(creds *Credentials, orderId string) (int64, error) {
	params := url.Values{}
	params.Add("fields", "order_number")
	order, err := s.GetOrder(orderId, creds, params)
	if err != nil {
		return 0, err
	}
	if order == nil {
		return 0, nil
	}
	return order.OrderNumber, nil
}

func (s *Shopify) GetOrder(orderId string, creds *Credentials, params url.Values) (*Order, error) {
	uri, err := s.getUri(fmt.Sprintf(OrderEndpoint, orderId), creds, params)
	if err != nil {
		return nil, err
	}
	var orderResponse *OrderResponse
	err = s.DoRequest("GET", uri, creds, nil, &orderResponse)
	if err != nil {
		return nil, fmt.Errorf("Request to Shopify Get Order failed: %s", err.Error())
	}
	// if no order returned, then just return nil. Do not error out.
	if orderResponse == nil {
		return nil, nil
	}
	return orderResponse.Order, nil
}
<commit_msg>Added limit parameter to FindOrderIdFromOrderNumber method<commit_after>package goshopify

import (
	"errors"
	"fmt"
	"net/url"
)

type OrdersCount struct {
	Count int64 `json:"count"`
}

type OrderResponse struct {
	Order *Order `json:"order"`
}

type OrdersResponse struct {
	Orders []*Order `json:"orders"`
}

type Order struct {
	BuyerAcceptsMarketing bool             `json:"buyer_accepts_marketing"`
	CancelReason          string           `json:"cancel_reason"`
	CancelledAt           string           `json:"cancelled_at"`
	CartToken             string           `json:"cart_token"`
	CheckoutToken         string           `json:"checkout_token"`
	ClosedAt              string           `json:"closed_at"`
	Confirmed             bool             `json:"confirmed"`
	CreatedAt             string           `json:"created_at"`
	Currency              string           `json:"currency"`
	DevicdId              int64            `json:"device_id"`
	Email                 string           `json:"email"`
	FinancialStatus       string           `json:"financial_status"`
	FulfillmentStatus     string           `json:"fulfillment_status"`
	Gateway               string           `json:"gateway"`
	Id                    int64            `json:"id"`
	LandingSite           string           `json:"landing_site"`
	LocationId            int64            `json:"location_id"`
	Name                  string           `json:"name"`
	Note                  string           `json:"note"`
	Number                int64            `json:"number"`
	ProcessedAt           string           `json:"processed_at"`
	Reference             string           `json:"reference"`
	ReferringSite         string           `json:"referring_site"`
	SourceIdentifier      string           `json:"source_identifier"`
	SourceUrl             string           `json:"source_url"`
	SubtotalPrice         string           `json:"subtotal_price"`
	TaxesIncluded         bool             `json:"taxes_included"`
	Test                  bool             `json:"test"`
	Token                 string           `json:"token"`
	TotalDiscount         string           `json:"total_discounts"`
	TotalLineItemsPrice   string           `json:"total_line_items_price"`
	TotalPrice            string           `json:"total_price"`
	TotalPriceUsd         string           `json:"total_price_usd"`
	TotalTax              string           `json:"total_tax"`
	TotalWeight           int64            `json:"total_weight"`
	UpdatedAt             string           `json:"updated_at"`
	UserId                int64            `json:"user_id"`
	BrowserIp             string           `json:"browser_ip"`
	LandingSiteRef        string           `json:"landing_site_ref"`
	OrderNumber           int64            `json:"order_number"`
	DiscountCodes         []*DiscountCode  `json:"discount_codes"`
	NoteAttributes        []*NoteAttribute `json:"note_attributes"`
	PaymentGatewayNames   []*string        `json:"payment_gateway_names"`
	ProcessingMethod      string           `json:"processing_method"`
	Source                string           `json:"source"`
	CheckoutId            int64            `json:"checkout_id"`
	SourceName            string           `json:"source_name"`
	TaxLines              []*TaxLine       `json:"tax_lines"`
	Tags                  string           `json:"tags"`
	LineItems             []*LineItem      `json:"line_items"`
	ShippingLines         []*ShippingLine  `json:"shipping_lines"`
	BillingAddress        *Address         `json:"billing_address"`
	ShippingAddress       *Address         `json:"shipping_address"`
	Fulfillments          []*Fulfillment   `json:"fulfillments"`
	ClientDetails         *ClientDetails   `json:"client_details"`
	Refunds               []*Refund        `json:"refunds"`
	Customer              *Customer        `json:"customer"`
}

type DiscountCode struct {
	Code   string `json:"code"`
	Amount string `json:"amount"`
	Type   string `json:"type"`
}

type NoteAttribute struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

type TaxLine struct {
	Price string  `json:"price"`
	Rate  float64 `json:"rate"`
	Title string  `json:"title"`
}

type LineItem struct {
	FulfillmentService         string      `json:"fulfillment_service"`
	FulfillmentStatus          string      `json:"fulfillment_status"`
	GiftCard                   bool        `json:"gift_card"`
	Grams                      float64     `json:"grams"`
	Id                         int64       `json:"id"`
	Price                      string      `json:"price"`
	ProductId                  int64       `json:"product_id"`
	Quantity                   int64       `json:"quantity"`
	RequiresShipping           bool        `json:"requires_shipping"`
	Sku                        string      `json:"sku"`
	Taxable                    bool        `json:"taxable"`
	Title                      string      `json:"title"`
	VariantId                  int64       `json:"variant_id"`
	VariantTitle               string      `json:"variant_title"`
	Vendor                     string      `json:"vendor"`
	Name                       string      `json:"name"`
	VariantInventoryManagement string      `json:"variant_inventory_management"`
	Properties                 []*Property `json:"properties"`
	ProductExists              bool        `json:"product_exists"`
	FulfillableQuantity        int64       `json:"fulfillable_quantity"`
	TotalDiscount              string      `json:"total_discount"`
	TaxLines                   []*TaxLine  `json:"tax_lines"`
}

type Property struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}

type ShippingLine struct {
	Code     string     `json:"code"`
	Price    string     `json:"price"`
	Source   string     `json:"source"`
	Title    string     `json:"title"`
	TaxLines []*TaxLine `json:"tax_lines"`
}

type Address struct {
	Address1     string  `json:"address1"`
	Address2     string  `json:"address2"`
	City         string  `json:"city"`
	Company      string  `json:"company"`
	Country      string  `json:"country"`
	FirstName    string  `json:"first_name"`
	LastName     string  `json:"last_name"`
	Latitude     float64 `json:"latitude"`
	Longitude    float64 `json:"longitude"`
	Phone        string  `json:"phone"`
	Province     string  `json:"province"`
	Zip          string  `json:"zip"`
	Name         string  `json:"name"`
	CountryCode  string  `json:"country_code"`
	ProvinceCode string  `json:"province_code"`
}

type ClientDetails struct {
	AcceptLanguage string `json:"accept_language"`
	BrowserHeight  int64  `json:"browser_height"`
	BrowserIp      string `json:"browser_ip"`
	BrowserWidth   int64  `json:"browser_width"`
	SessionHash    string `json:"session_hash"`
	UserAgent      string `json:"user_agent"`
}

type Refund struct {
	CreatedAt       string            `json:"created_at"`
	Id              int64             `json:"id"`
	Note            string            `json:"note"`
	OrderId         int64             `json:"order_id"`
	Restock         bool              `json:"restock"`
	UserId          int64             `json:"user_id"`
	RefundLineItems []*RefundLineItem `json:"refund_line_items"`
	Transactions    []*Transaction    `json:"transactions"`
}

type RefundLineItem struct {
	Id         int64     `json:"id"`
	LineItemId int64     `json:"line_item_id"`
	Quantity   int64     `json:"quantity"`
	LineItem   *LineItem `json:"line_item"`
}

func (s *Shopify) GetOrdersCount(creds *Credentials, params url.Values) (*OrdersCount, error) {
	uri, err := s.getUri(OrdersCountEndpoint, creds, params)
	if err != nil {
		return nil, err
	}

	var count *OrdersCount
	err = s.DoRequest("GET", uri, creds, nil, &count)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("OrdersCount: %s", err.Error()))
	}

	return count, nil
}

func (s *Shopify) GetOrders(creds *Credentials, params url.Values) ([]*Order, error) {
	uri, err := s.getUri(OrdersEndpoint, creds, params)
	if err != nil {
		return nil, err
	}

	var ordersResponse *OrdersResponse
	err = s.DoRequest("GET", uri, creds, nil, &ordersResponse)
	if err != nil {
		return nil, fmt.Errorf("Request to Shopify Orders failed: %s", err.Error())
	}

	return ordersResponse.Orders, nil
}

func (s *Shopify) GetOrdersSinceId(creds *Credentials, sinceId string, params url.Values) ([]*Order, error) {
	if params == nil {
		params = url.Values{}
	}
	params.Add("since_id", sinceId)
	return s.GetOrders(creds, params)
}

func (s *Shopify) GetOrdersSinceOrderNumber(creds *Credentials, sinceNum string, params url.Values) ([]*Order, error) {
	sinceId, err := s.FindOrderIdFromOrderNumber(creds, sinceNum)
	if err != nil {
		return nil, err
	}
	return s.GetOrdersSinceId(creds, sinceId, params)
}

func (s *Shopify) FindOrderIdFromOrderNumber(creds *Credentials, orderNum string) (string, error) {
	params := url.Values{}
	params.Add("name", orderNum)
	params.Add("status", "any")
	params.Add("fields", "id,order_number")
	params.Add("limit", goshopify.MaxLimit)
	uri, err := s.getUri(OrdersEndpoint, creds, params)
	var ordersResponse *OrdersResponse
	err = s.DoRequest("GET", uri, creds, nil, &ordersResponse)
	if err != nil {
		return "", fmt.Errorf("Request to Shopify Orders failed: %s", err.Error())
	}
	if ordersResponse == nil || ordersResponse.Orders == nil || len(ordersResponse.Orders) != 1 {
		return "", fmt.Errorf("invalid response from shopify when resolving orders")
	}
	return fmt.Sprintf("%v", ordersResponse.Orders[0].Id), nil
}

func (s *Shopify) FindOrderNumberFromOrderId(creds *Credentials, orderId string) (int64, error) {
	params := url.Values{}
	params.Add("fields", "order_number")
	order, err := s.GetOrder(orderId, creds, params)
	if err != nil {
		return 0, err
	}
	if order == nil {
		return 0, nil
	}
	return order.OrderNumber, nil
}

func (s *Shopify) GetOrder(orderId string, creds *Credentials, params url.Values) (*Order, error) {
	uri, err := s.getUri(fmt.Sprintf(OrderEndpoint, orderId), creds, params)
	if err != nil {
		return nil, err
	}
	var orderResponse *OrderResponse
	err = s.DoRequest("GET", uri, creds, nil, &orderResponse)
	if err != nil {
		return nil, fmt.Errorf("Request to Shopify Get Order failed: %s", err.Error())
	}
	// if no order returned, then just return nil. Do not error out.
	if orderResponse == nil {
		return nil, nil
	}
	return orderResponse.Order, nil
}
<|endoftext|>


<commit_before>package xlog

import (
	"encoding/json"
	"io"
	"log"
	"log/syslog"
	"os"
	"strings"
)

// Output sends a log message fields to its destination
type Output interface {
	Write(fields map[string]interface{}) error
}

type discard struct{}

func (o discard) Write(fields map[string]interface{}) (err error) {
	return nil
}

// Discard discards log output
var Discard = &discard{}

// MultiOutput routes the same message to serveral outputs
// If one or more outputs return error, the last error is returned.
type MultiOutput []Output

func (m MultiOutput) Write(fields map[string]interface{}) (err error) {
	for _, o := range m {
		e := o.Write(fields)
		if e != nil {
			err = e
		}
	}
	return
}

// FilterOutput test a condition on the message and forward it to the child output
// if it returns true
type FilterOutput struct {
	Cond   func(fields map[string]interface{}) bool
	Output Output
}

func (f FilterOutput) Write(fields map[string]interface{}) (err error) {
	if f.Output == nil {
		return
	}
	if f.Cond(fields) {
		return f.Output.Write(fields)
	}
	return
}

// LevelOutput routes messages per level outputs
type LevelOutput struct {
	Debug Output
	Info  Output
	Warn  Output
	Error Output
}

func (l LevelOutput) Write(fields map[string]interface{}) error {
	var o Output
	switch fields[KeyLevel] {
	case "debug":
		o = l.Debug
	case "info":
		o = l.Info
	case "warn":
		o = l.Warn
	case "error":
		o = l.Error
	}
	if o != nil {
		return o.Write(fields)
	}
	return nil
}

// NewSyslogOutput returns JSONOutputs in a LevelOutput with writers set to syslog
// with the proper priority.
// If network and address are empty, Dial will connect to the local syslog server.
func NewSyslogOutput(network, address, tag string) Output {
	var err error
	o := LevelOutput{}
	if o.Debug, err = newJSONSyslogOutput(network, address, syslog.LOG_DEBUG, tag); err != nil {
		log.Panicf("xlog: syslog error: %v", err)
	}
	if o.Info, err = newJSONSyslogOutput(network, address, syslog.LOG_INFO, tag); err != nil {
		log.Panicf("xlog: syslog error: %v", err)
	}
	if o.Warn, err = newJSONSyslogOutput(network, address, syslog.LOG_WARNING, tag); err != nil {
		log.Panicf("xlog: syslog error: %v", err)
	}
	if o.Error, err = newJSONSyslogOutput(network, address, syslog.LOG_ERR, tag); err != nil {
		log.Panicf("xlog: syslog error: %v", err)
	}
	return o
}

func newJSONSyslogOutput(network, address string, prio syslog.Priority, tag string) (Output, error) {
	s, err := syslog.Dial(network, address, prio, tag)
	if err != nil {
		return nil, err
	}
	return NewJSONOutput(s), nil
}

// ConsoleOutput writes the message key if present followed by other fields in a
// given io.Writer.
type ConsoleOutput struct {
	w io.Writer
}

// NewConsoleOutput returns ConsoleOutputs in a LevelOutput with error levels on os.Stderr
// and other on os.Stdin
func NewConsoleOutput() Output {
	o := ConsoleOutput{w: os.Stdout}
	e := ConsoleOutput{w: os.Stderr}
	return LevelOutput{
		Debug: o,
		Info:  o,
		Warn:  e,
		Error: e,
	}
}

func (o ConsoleOutput) Write(fields map[string]interface{}) error {
	if msg, ok := fields[KeyMessage].(string); ok {
		delete(fields, KeyMessage)
		msg = strings.Replace(msg, "\n", "\\n", -1)
		o.w.Write([]byte(msg + " "))
	}
	b, err := json.Marshal(fields)
	if err != nil {
		return err
	}
	if _, err = o.w.Write(b); err != nil {
		return err
	}
	o.w.Write([]byte{'\n'})
	return nil
}

// JSONOutput marshals message fields and write the result on an io.Writer
type JSONOutput struct {
	w io.Writer
}

// NewJSONOutput returns a new JSONOutput with the given writer
func NewJSONOutput(w io.Writer) Output {
	return JSONOutput{w: w}
}

func (o JSONOutput) Write(fields map[string]interface{}) error {
	b, err := json.Marshal(fields)
	if err != nil {
		return err
	}
	if _, err = o.w.Write(b); err != nil {
		return err
	}
	o.w.Write([]byte{'\n'})
	return nil
}
<commit_msg>Fix typo<commit_after>package xlog

import (
	"encoding/json"
	"io"
	"log"
	"log/syslog"
	"os"
	"strings"
)

// Output sends a log message fields to its destination
type Output interface {
	Write(fields map[string]interface{}) error
}

type discard struct{}

func (o discard) Write(fields map[string]interface{}) (err error) {
	return nil
}

// Discard discards log output
var Discard = &discard{}

// MultiOutput routes the same message to serveral outputs.
// If one or more outputs return error, the last error is returned.
type MultiOutput []Output

func (m MultiOutput) Write(fields map[string]interface{}) (err error) {
	for _, o := range m {
		e := o.Write(fields)
		if e != nil {
			err = e
		}
	}
	return
}

// FilterOutput test a condition on the message and forward it to the child output
// if it returns true
type FilterOutput struct {
	Cond   func(fields map[string]interface{}) bool
	Output Output
}

func (f FilterOutput) Write(fields map[string]interface{}) (err error) {
	if f.Output == nil {
		return
	}
	if f.Cond(fields) {
		return f.Output.Write(fields)
	}
	return
}

// LevelOutput routes messages per level outputs
type LevelOutput struct {
	Debug Output
	Info  Output
	Warn  Output
	Error Output
}

func (l LevelOutput) Write(fields map[string]interface{}) error {
	var o Output
	switch fields[KeyLevel] {
	case "debug":
		o = l.Debug
	case "info":
		o = l.Info
	case "warn":
		o = l.Warn
	case "error":
		o = l.Error
	}
	if o != nil {
		return o.Write(fields)
	}
	return nil
}

// NewSyslogOutput returns JSONOutputs in a LevelOutput with writers set to syslog
// with the proper priority.
// If network and address are empty, Dial will connect to the local syslog server.
func NewSyslogOutput(network, address, tag string) Output {
	var err error
	o := LevelOutput{}
	if o.Debug, err = newJSONSyslogOutput(network, address, syslog.LOG_DEBUG, tag); err != nil {
		log.Panicf("xlog: syslog error: %v", err)
	}
	if o.Info, err = newJSONSyslogOutput(network, address, syslog.LOG_INFO, tag); err != nil {
		log.Panicf("xlog: syslog error: %v", err)
	}
	if o.Warn, err = newJSONSyslogOutput(network, address, syslog.LOG_WARNING, tag); err != nil {
		log.Panicf("xlog: syslog error: %v", err)
	}
	if o.Error, err = newJSONSyslogOutput(network, address, syslog.LOG_ERR, tag); err != nil {
		log.Panicf("xlog: syslog error: %v", err)
	}
	return o
}

func newJSONSyslogOutput(network, address string, prio syslog.Priority, tag string) (Output, error) {
	s, err := syslog.Dial(network, address, prio, tag)
	if err != nil {
		return nil, err
	}
	return NewJSONOutput(s), nil
}

// ConsoleOutput writes the message key if present followed by other fields in a
// given io.Writer.
type ConsoleOutput struct {
	w io.Writer
}

// NewConsoleOutput returns ConsoleOutputs in a LevelOutput with error levels on os.Stderr
// and other on os.Stdin
func NewConsoleOutput() Output {
	o := ConsoleOutput{w: os.Stdout}
	e := ConsoleOutput{w: os.Stderr}
	return LevelOutput{
		Debug: o,
		Info:  o,
		Warn:  e,
		Error: e,
	}
}

func (o ConsoleOutput) Write(fields map[string]interface{}) error {
	if msg, ok := fields[KeyMessage].(string); ok {
		delete(fields, KeyMessage)
		msg = strings.Replace(msg, "\n", "\\n", -1)
		o.w.Write([]byte(msg + " "))
	}
	b, err := json.Marshal(fields)
	if err != nil {
		return err
	}
	if _, err = o.w.Write(b); err != nil {
		return err
	}
	o.w.Write([]byte{'\n'})
	return nil
}

// JSONOutput marshals message fields and write the result on an io.Writer
type JSONOutput struct {
	w io.Writer
}

// NewJSONOutput returns a new JSONOutput with the given writer
func NewJSONOutput(w io.Writer) Output {
	return JSONOutput{w: w}
}

func (o JSONOutput) Write(fields map[string]interface{}) error {
	b, err := json.Marshal(fields)
	if err != nil {
		return err
	}
	if _, err = o.w.Write(b); err != nil {
		return err
	}
	o.w.Write([]byte{'\n'})
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Pipe adapter to connect code expecting an io.Reader
// with code expecting an io.Writer.

package nio

import (
	"errors"
	"io"
	"sync"
	"time"
)

// ErrClosedPipe is the error used for read or write operations on a closed pipe.
var ErrClosedPipe = errors.New("io: read/write on closed pipe")
var ErrReadTimeout = errors.New("read timeout")
var ErrWriteTimeout = errors.New("write timeout")

type pipeResult struct {
	n   int
	err error
}

var signal struct{}

// A pipe is the shared pipe structure underlying PipeReader and PipeWriter.
type pipe struct {
	rl    sync.Mutex    // gates readers one at a time
	wl    sync.Mutex    // gates writers one at a time
	l     sync.Mutex    // protects remaining fields
	rt    *time.Timer   // read timeout
	wt    *time.Timer   // write timeout
	rdt   time.Time     // read deadline
	wdt   time.Time     // write deadline
	rtc   chan struct{} // read timeout channel
	wtc   chan struct{} // write timeout channel
	data  []byte        // data remaining in pending write
	rwait sync.Cond     // waiting reader
	wwait sync.Cond     // waiting writer
	rerr  error         // if reader closed, error to give writes
	werr  error         // if writer closed, error to give reads
}

func (p *pipe) readTimeout() {
	if !p.rdt.IsZero() {
		if p.rt == nil {
			p.rt = time.AfterFunc(p.rdt.Sub(time.Now()), func() {
				p.rtc <- signal
				p.rwait.Signal()
			})
		} else {
			p.rt.Reset(p.rdt.Sub(time.Now()))
		}
	}
}

func (p *pipe) read(b []byte) (n int, err error) {
	// One reader at a time.
	p.rl.Lock()
	defer p.rl.Unlock()

	p.l.Lock()
	defer p.l.Unlock()

	p.readTimeout()

	for {
		select {
		case <-p.rtc:
			// BUG(Dustin): Clear state.
			return 0, ErrReadTimeout
		default:
		}

		if p.rerr != nil {
			return 0, ErrClosedPipe
		}
		if p.data != nil {
			break
		}
		if p.werr != nil {
			return 0, p.werr
		}
		p.rwait.Wait()
	}
	n = copy(b, p.data)
	p.data = p.data[n:]
	if len(p.data) == 0 {
		p.data = nil
		if p.wt != nil {
			p.wt.Stop()
		}
		p.wwait.Signal()
	}
	return
}

var zero [0]byte

func (p *pipe) writeTimeout() {
	if !p.wdt.IsZero() {
		if p.wt == nil {
			p.wt = time.AfterFunc(p.wdt.Sub(time.Now()), func() {
				p.wtc <- signal
				p.wwait.Signal()
			})
		} else {
			p.wt.Reset(p.wdt.Sub(time.Now()))
		}
	}
}

func (p *pipe) write(b []byte) (n int, err error) {
	// pipe uses nil to mean not available
	if b == nil {
		b = zero[:]
	}

	// One writer at a time.
	p.wl.Lock()
	defer p.wl.Unlock()

	p.l.Lock()
	defer p.l.Unlock()

	p.writeTimeout()

	if p.werr != nil {
		err = ErrClosedPipe
		return
	}
	p.data = b
	if p.rt != nil {
		p.rt.Stop()
	}
	p.rwait.Signal()
	for {
		select {
		case <-p.wtc:
			// BUG(Dustin): Clear state.
			n = len(b) - len(p.data)
			p.data = nil
			return n, ErrWriteTimeout
		default:
		}

		if p.data == nil {
			break
		}
		if p.rerr != nil {
			err = p.rerr
			break
		}
		if p.werr != nil {
			err = ErrClosedPipe
		}
		p.wwait.Wait()
	}
	n = len(b) - len(p.data)
	p.data = nil // in case of rerr or werr
	return
}

func (p *pipe) rclose(err error) {
	if err == nil {
		err = ErrClosedPipe
	}
	p.l.Lock()
	defer p.l.Unlock()
	p.rerr = err
	p.rwait.Signal()
	p.wwait.Signal()
}

func (p *pipe) wclose(err error) {
	if err == nil {
		err = io.EOF
	}
	p.l.Lock()
	defer p.l.Unlock()
	p.werr = err
	p.rwait.Signal()
	p.wwait.Signal()
}

// A PipeReader is the read half of a pipe.
type PipeReader struct {
	p *pipe
}

func (w *PipeReader) SetReadDeadline(t time.Time) error {
	w.p.l.Lock()
	defer w.p.l.Unlock()
	w.p.rdt = t
	if w.p.rt != nil {
		w.p.rt.Reset(t.Sub(time.Now()))
	}
	return nil
}

// Read implements the standard Read interface:
// it reads data from the pipe, blocking until a writer
// arrives or the write end is closed.
// If the write end is closed with an error, that error is
// returned as err; otherwise err is EOF.
func (r *PipeReader) Read(data []byte) (n int, err error) {
	return r.p.read(data)
}

// Close closes the reader; subsequent writes to the
// write half of the pipe will return the error ErrClosedPipe.
func (r *PipeReader) Close() error {
	return r.CloseWithError(nil)
}

// CloseWithError closes the reader; subsequent writes
// to the write half of the pipe will return the error err.
func (r *PipeReader) CloseWithError(err error) error {
	r.p.rclose(err)
	return nil
}

// A PipeWriter is the write half of a pipe.
type PipeWriter struct {
	p *pipe
}

func (w *PipeWriter) SetWriteDeadline(t time.Time) error {
	w.p.l.Lock()
	defer w.p.l.Unlock()
	w.p.wdt = t
	if w.p.wt != nil {
		w.p.wt.Reset(t.Sub(time.Now()))
	}
	return nil
}

// Write implements the standard Write interface:
// it writes data to the pipe, blocking until readers
// have consumed all the data or the read end is closed.
// If the read end is closed with an error, that err is
// returned as err; otherwise err is ErrClosedPipe.
func (w *PipeWriter) Write(data []byte) (n int, err error) {
	return w.p.write(data)
}

// Close closes the writer; subsequent reads from the
// read half of the pipe will return no bytes and EOF.
func (w *PipeWriter) Close() error {
	return w.CloseWithError(nil)
}

// CloseWithError closes the writer; subsequent reads from the
// read half of the pipe will return no bytes and the error err.
func (w *PipeWriter) CloseWithError(err error) error {
	w.p.wclose(err)
	return nil
}

// Pipe creates a synchronous in-memory pipe.
// It can be used to connect code expecting an io.Reader
// with code expecting an io.Writer.
// Reads on one end are matched with writes on the other,
// copying data directly between the two; there is no internal buffering.
// It is safe to call Read and Write in parallel with each other or with
// Close. Close will complete once pending I/O is done. Parallel calls to
// Read, and parallel calls to Write, are also safe:
// the individual calls will be gated sequentially.
func TimeoutPipe() (*PipeReader, *PipeWriter) {
	p := new(pipe)
	p.rwait.L = &p.l
	p.wwait.L = &p.l
	p.rtc = make(chan struct{}, 1)
	p.wtc = make(chan struct{}, 1)
	r := &PipeReader{p}
	w := &PipeWriter{p}
	return r, w
}
<commit_msg>Updated godoc<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Pipe adapter to connect code expecting an io.Reader
// with code expecting an io.Writer.

package nio

import (
	"errors"
	"io"
	"sync"
	"time"
)

// ErrClosedPipe is the error used for read or write operations on a closed pipe.
var ErrClosedPipe = errors.New("io: read/write on closed pipe")
var ErrReadTimeout = errors.New("read timeout")
var ErrWriteTimeout = errors.New("write timeout")

type pipeResult struct {
	n   int
	err error
}

var signal struct{}

// A pipe is the shared pipe structure underlying PipeReader and PipeWriter.
type pipe struct {
	rl    sync.Mutex    // gates readers one at a time
	wl    sync.Mutex    // gates writers one at a time
	l     sync.Mutex    // protects remaining fields
	rt    *time.Timer   // read timeout
	wt    *time.Timer   // write timeout
	rdt   time.Time     // read deadline
	wdt   time.Time     // write deadline
	rtc   chan struct{} // read timeout channel
	wtc   chan struct{} // write timeout channel
	data  []byte        // data remaining in pending write
	rwait sync.Cond     // waiting reader
	wwait sync.Cond     // waiting writer
	rerr  error         // if reader closed, error to give writes
	werr  error         // if writer closed, error to give reads
}

func (p *pipe) readTimeout() {
	if !p.rdt.IsZero() {
		if p.rt == nil {
			p.rt = time.AfterFunc(p.rdt.Sub(time.Now()), func() {
				p.rtc <- signal
				p.rwait.Signal()
			})
		} else {
			p.rt.Reset(p.rdt.Sub(time.Now()))
		}
	}
}

func (p *pipe) read(b []byte) (n int, err error) {
	// One reader at a time.
	p.rl.Lock()
	defer p.rl.Unlock()

	p.l.Lock()
	defer p.l.Unlock()

	p.readTimeout()

	for {
		select {
		case <-p.rtc:
			// BUG(Dustin): Clear state.
			return 0, ErrReadTimeout
		default:
		}

		if p.rerr != nil {
			return 0, ErrClosedPipe
		}
		if p.data != nil {
			break
		}
		if p.werr != nil {
			return 0, p.werr
		}
		p.rwait.Wait()
	}
	n = copy(b, p.data)
	p.data = p.data[n:]
	if len(p.data) == 0 {
		p.data = nil
		if p.wt != nil {
			p.wt.Stop()
		}
		p.wwait.Signal()
	}
	return
}

var zero [0]byte

func (p *pipe) writeTimeout() {
	if !p.wdt.IsZero() {
		if p.wt == nil {
			p.wt = time.AfterFunc(p.wdt.Sub(time.Now()), func() {
				p.wtc <- signal
				p.wwait.Signal()
			})
		} else {
			p.wt.Reset(p.wdt.Sub(time.Now()))
		}
	}
}

func (p *pipe) write(b []byte) (n int, err error) {
	// pipe uses nil to mean not available
	if b == nil {
		b = zero[:]
	}

	// One writer at a time.
	p.wl.Lock()
	defer p.wl.Unlock()

	p.l.Lock()
	defer p.l.Unlock()

	p.writeTimeout()

	if p.werr != nil {
		err = ErrClosedPipe
		return
	}
	p.data = b
	if p.rt != nil {
		p.rt.Stop()
	}
	p.rwait.Signal()
	for {
		select {
		case <-p.wtc:
			// BUG(Dustin): Clear state.
			n = len(b) - len(p.data)
			p.data = nil
			return n, ErrWriteTimeout
		default:
		}

		if p.data == nil {
			break
		}
		if p.rerr != nil {
			err = p.rerr
			break
		}
		if p.werr != nil {
			err = ErrClosedPipe
		}
		p.wwait.Wait()
	}
	n = len(b) - len(p.data)
	p.data = nil // in case of rerr or werr
	return
}

func (p *pipe) rclose(err error) {
	if err == nil {
		err = ErrClosedPipe
	}
	p.l.Lock()
	defer p.l.Unlock()
	p.rerr = err
	p.rwait.Signal()
	p.wwait.Signal()
}

func (p *pipe) wclose(err error) {
	if err == nil {
		err = io.EOF
	}
	p.l.Lock()
	defer p.l.Unlock()
	p.werr = err
	p.rwait.Signal()
	p.wwait.Signal()
}

// A PipeReader is the read half of a pipe.
type PipeReader struct {
	p *pipe
}

func (w *PipeReader) SetReadDeadline(t time.Time) error {
	w.p.l.Lock()
	defer w.p.l.Unlock()
	w.p.rdt = t
	if w.p.rt != nil {
		w.p.rt.Reset(t.Sub(time.Now()))
	}
	return nil
}

// Read implements the standard Read interface:
// it reads data from the pipe, blocking until a writer
// arrives or the write end is closed.
// If the write end is closed with an error, that error is
// returned as err; otherwise err is EOF.
func (r *PipeReader) Read(data []byte) (n int, err error) {
	return r.p.read(data)
}

// Close closes the reader; subsequent writes to the
// write half of the pipe will return the error ErrClosedPipe.
func (r *PipeReader) Close() error {
	return r.CloseWithError(nil)
}

// CloseWithError closes the reader; subsequent writes
// to the write half of the pipe will return the error err.
func (r *PipeReader) CloseWithError(err error) error {
	r.p.rclose(err)
	return nil
}

// A PipeWriter is the write half of a pipe.
type PipeWriter struct {
	p *pipe
}

func (w *PipeWriter) SetWriteDeadline(t time.Time) error {
	w.p.l.Lock()
	defer w.p.l.Unlock()
	w.p.wdt = t
	if w.p.wt != nil {
		w.p.wt.Reset(t.Sub(time.Now()))
	}
	return nil
}

// Write implements the standard Write interface:
// it writes data to the pipe, blocking until readers
// have consumed all the data or the read end is closed.
// If the read end is closed with an error, that err is
// returned as err; otherwise err is ErrClosedPipe.
func (w *PipeWriter) Write(data []byte) (n int, err error) {
	return w.p.write(data)
}

// Close closes the writer; subsequent reads from the
// read half of the pipe will return no bytes and EOF.
func (w *PipeWriter) Close() error {
	return w.CloseWithError(nil)
}

// CloseWithError closes the writer; subsequent reads from the
// read half of the pipe will return no bytes and the error err.
func (w *PipeWriter) CloseWithError(err error) error {
	w.p.wclose(err)
	return nil
}

// TimeoutPipe creates a synchronous in-memory pipe with optional Read/Write Deadlines.
// It can be used to connect code expecting an io.Reader
// with code expecting an io.Writer.
// Reads on one end are matched with writes on the other,
// copying data directly between the two; there is no internal buffering.
// It is safe to call Read and Write in parallel with each other or with
// Close. Close will complete once pending I/O is done. Parallel calls to
// Read, and parallel calls to Write, are also safe:
// the individual calls will be gated sequentially.
func TimeoutPipe() (*PipeReader, *PipeWriter) {
	p := new(pipe)
	p.rwait.L = &p.l
	p.wwait.L = &p.l
	p.rtc = make(chan struct{}, 1)
	p.wtc = make(chan struct{}, 1)
	r := &PipeReader{p}
	w := &PipeWriter{p}
	return r, w
}
<|endoftext|>


<commit_before>// Copyright 2015 Dorival de Moraes Pedroso. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goga

import (
	"encoding/json"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
	"github.com/cpmech/gosl/rnd"
)

// Parameters hold all configuration parameters
type Parameters struct {

	// sizes
	Nova int // number of objective values
	Noor int // number of out-of-range values
	Nsol int // total number of solutions
	Ncpu int // number of cpus

	// time
	Tf    int // final time
	DtExc int // delta time for exchange
	DtOut int // delta time for output

	// options
	Pll      bool    // parallel
	Seed     int     // seed for random numbers generator
	GenType  string  // generation type: "latin", "halton", "rnd"
	LatinDup int     // Latin Hypercube duplicates number
	EpsH     float64 // minimum value for 'h' constraints
	Verbose  bool    // show messages
	GenAll   bool    // generate all solutions together; i.e. not within each group/CPU
	Ntrials  int     // run many trials
	BinInt   int     // flag that integers represent binary numbers if BinInt > 0; thus Nint=BinInt
	ClearFlt bool    // clear flt if corresponding int is 0
	ExcTour  bool    // use exchange via tournament
	ExcOne   bool    // use exchange one randomly

	// differential evolution
	DiffEvolC        float64 // crossover probability
	DiffEvolF        float64 // vector length multiplier
	DiffEvolPm       float64 // mutation probability. use rotation otherwise
	DiffEvolUseCmult bool    // use C random multiplier
	DiffEvolUseFmult bool    // use F random multiplier

	// crossover and mutation
	PmInt float64 // probability of mutation for ints
	PcInt float64 // probability of crossover for ints

	// range
	FltMin []float64 // minimum float allowed
	FltMax []float64 // maximum float allowed
	IntMin []int     // minimum int allowed
	IntMax []int     // maximum int allowed

	// derived
	Nflt   int       // number of floats
	Nint   int       // number of integers
	DelFlt []float64 // max float range
	DelInt []int     // max int range
}

// Default sets default parameters
func (o *Parameters) Default() {

	// sizes
	o.Nova = 1
	o.Noor = 0
	o.Nsol = 40
	o.Ncpu = 4

	// time
	o.Tf = 100
	o.DtExc = -1
	o.DtOut = -1

	// options
	o.Pll = true
	o.Seed = 0
	o.GenType = "latin"
	o.LatinDup = 2
	o.EpsH = 0.1
	o.Verbose = true
	o.GenAll = false
	o.Ntrials = 10
	o.BinInt = 0
	o.ClearFlt = false
	o.ExcTour = true
	o.ExcOne = false

	// differential evolution
	o.DiffEvolC = 1.0
	o.DiffEvolF = 1.0
	o.DiffEvolPm = 1.0
	o.DiffEvolUseCmult = true
	o.DiffEvolUseFmult = true

	// crossover and mutation
	o.PmInt = 0.01
	o.PcInt = 0.8
}

// Read reads configuration parameters from JSON file
func (o *Parameters) Read(filenamepath string) {
	o.Default()
	b, err := io.ReadFile(filenamepath)
	if err != nil {
		chk.Panic("cannot read parameters file %q", filenamepath)
	}
	err = json.Unmarshal(b, o)
	if err != nil {
		chk.Panic("cannot unmarshal parameters file %q", filenamepath)
	}
	return
}

// CalcDerived computes derived variables and checks consistency
func (o *Parameters) CalcDerived() {

	// check
	if o.Nova < 1 {
		chk.Panic("number of objective values (nova) must be greater than 0")
	}
	if o.Nsol < 6 {
		chk.Panic("number of solutions must greater than 6. Nsol = %d is invalid", o.Nsol)
	}
	if o.Ncpu < 2 {
		o.Ncpu = 1
		o.Pll = false
		o.DtExc = 1
	}
	if o.Ncpu > o.Nsol/2 {
		chk.Panic("number of CPU must be smaller than or equal to half the number of solutions. Ncpu=%d > Nsol/2=%d", o.Ncpu, o.Nsol/2)
	}
	if o.Tf < 1 {
		o.Tf = 1
	}
	if o.DtExc < 1 {
		o.DtExc = o.Tf / 10
	}
	if o.DtOut < 1 {
		o.DtOut = o.Tf / 5
	}

	// derived
	o.Nflt = len(o.FltMin)
	o.Nint = len(o.IntMin)
	if o.BinInt > 0 {
		o.Nint = o.BinInt
	}
	if o.Nflt == 0 && o.Nint == 0 {
		chk.Panic("either floats and ints must be set (via FltMin/Max or IntMin/Max)")
	}

	// floats
	if o.Nflt > 0 {
		chk.IntAssert(len(o.FltMax), o.Nflt)
		o.DelFlt = make([]float64, o.Nflt)
		for i := 0; i < o.Nflt; i++ {
			o.DelFlt[i] = o.FltMax[i] - o.FltMin[i]
		}
	}

	// generic ints
	if o.BinInt == 0 && o.Nint > 0 {
		chk.IntAssert(len(o.IntMax), o.Nint)
		o.DelInt = make([]int, o.Nint)
		for i := 0; i < o.Nint; i++ {
			o.DelInt[i] = o.IntMax[i] - o.IntMin[i]
		}
	}
	if o.Nint != o.Nflt {
		o.ClearFlt = false
	}

	// initialise random numbers generator
	rnd.Init(o.Seed)
}

// EnforceRange makes sure x is within given range
func (o *Parameters) EnforceRange(i int, x float64) float64 {
	if x < o.FltMin[i] {
		return o.FltMin[i]
	}
	if x > o.FltMax[i] {
		return o.FltMax[i]
	}
	return x
}

// LogParams returns a log with current parameters
func (o *Parameters) LogParams() (l string) {

	// sizes
	l += io.ArgsTable("SIZES",
		"number of objective values", "Nova", o.Nova,
		"number of out-of-range values", "Noor", o.Noor,
		"total number of solutions", "Nsol", o.Nsol,
		"number of cpus", "Ncpu", o.Ncpu,
	)

	// time
	l += "\n"
	l += io.ArgsTable("TIME",
		"final time", "Tf", o.Tf,
		"delta time for exchange", "DtExc", o.DtExc,
		"delta time for output", "DtOut", o.DtOut,
	)

	// options
	l += "\n"
	l += io.ArgsTable("OPTIONS",
		"parallel", "Pll", o.Pll,
		"seed for random numbers generator", "Seed", o.Seed,
		"generation type: 'latin', 'halton', 'rnd'", "GenType", o.GenType,
		"Latin Hypercube duplicates number", "LatinDup", o.LatinDup,
		"minimum value for 'h' constraints", "EpsH", o.EpsH,
		"show messages", "Verbose", o.Verbose,
		"generate all solutions together", "GenAll", o.GenAll,
		"run many trials", "Ntrials", o.Ntrials,
		"integers represent binary numbers", "BinInt", o.BinInt,
		"clear flt if corresponding int is 0", "ClearFlt", o.ClearFlt,
		"use exchange via tournament", "ExcTour", o.ExcTour,
		"use exchange one randomly", "ExcOne", o.ExcOne,
	)

	// differential evolution
	l += "\n"
	l += io.ArgsTable("DIFFERENTIAL EVOLUTION",
		"crossover probability", "DiffEvolC", o.DiffEvolC,
		"vector length multiplier", "DiffEvolF", o.DiffEvolF,
		"mutation probability", "DiffEvolPm", o.DiffEvolPm,
		"use C random multiplier", "DiffEvolUseCmult", o.DiffEvolUseCmult,
		"use F random multiplier", "DiffEvolUseFmult", o.DiffEvolUseFmult,
	)

	// crossover and mutation
	l += "\n"
	l += io.ArgsTable("CROSSOVER AND MUTATION",
		"probability of mutation for ints", "PmInt", o.PmInt,
		"probability of crossover for ints", "PcInt", o.PcInt,
	)

	// derived
	l += "\n"
	l += io.ArgsTable("DERIVED",
		"number of floats", "Nflt", o.Nflt,
		"number of integers", "Nint", o.Nint,
	)
	return
}
<commit_msg>(unused) DE paramters removed<commit_after>// Copyright 2015 Dorival de Moraes Pedroso. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goga

import (
	"encoding/json"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
	"github.com/cpmech/gosl/rnd"
)

// Parameters hold all configuration parameters
type Parameters struct {

	// sizes
	Nova int // number of objective values
	Noor int // number of out-of-range values
	Nsol int // total number of solutions
	Ncpu int // number of cpus

	// time
	Tf    int // final time
	DtExc int // delta time for exchange
	DtOut int // delta time for output

	// options
	Pll      bool    // parallel
	Seed     int     // seed for random numbers generator
	GenType  string  // generation type: "latin", "halton", "rnd"
	LatinDup int     // Latin Hypercube duplicates number
	EpsH     float64 // minimum value for 'h' constraints
	Verbose  bool    // show messages
	GenAll   bool    // generate all solutions together; i.e. not within each group/CPU
	Ntrials  int     // run many trials
	BinInt   int     // flag that integers represent binary numbers if BinInt > 0; thus Nint=BinInt
	ClearFlt bool    // clear flt if corresponding int is 0
	ExcTour  bool    // use exchange via tournament
	ExcOne   bool    // use exchange one randomly

	// crossover and mutation
	PmInt float64 // probability of mutation for ints
	PcInt float64 // probability of crossover for ints

	// range
	FltMin []float64 // minimum float allowed
	FltMax []float64 // maximum float allowed
	IntMin []int     // minimum int allowed
	IntMax []int     // maximum int allowed

	// derived
	Nflt   int       // number of floats
	Nint   int       // number of integers
	DelFlt []float64 // max float range
	DelInt []int     // max int range
}

// Default sets default parameters
func (o *Parameters) Default() {

	// sizes
	o.Nova = 1
	o.Noor = 0
	o.Nsol = 40
	o.Ncpu = 4

	// time
	o.Tf = 100
	o.DtExc = -1
	o.DtOut = -1

	// options
	o.Pll = true
	o.Seed = 0
	o.GenType = "latin"
	o.LatinDup = 2
	o.EpsH = 0.1
	o.Verbose = true
	o.GenAll = false
	o.Ntrials = 10
	o.BinInt = 0
	o.ClearFlt = false
	o.ExcTour = true
	o.ExcOne = false

	// crossover and mutation
	o.PmInt = 0.01
	o.PcInt = 0.8
}

// Read reads configuration parameters from JSON file
func (o *Parameters) Read(filenamepath string) {
	o.Default()
	b, err := io.ReadFile(filenamepath)
	if err != nil {
		chk.Panic("cannot read parameters file %q", filenamepath)
	}
	err = json.Unmarshal(b, o)
	if err != nil {
		chk.Panic("cannot unmarshal parameters file %q", filenamepath)
	}
	return
}

// CalcDerived computes derived variables and checks consistency
func (o *Parameters) CalcDerived() {

	// check
	if o.Nova < 1 {
		chk.Panic("number of objective values (nova) must be greater than 0")
	}
	if o.Nsol < 6 {
		chk.Panic("number of solutions must greater than 6. Nsol = %d is invalid", o.Nsol)
	}
	if o.Ncpu < 2 {
		o.Ncpu = 1
		o.Pll = false
		o.DtExc = 1
	}
	if o.Ncpu > o.Nsol/2 {
		chk.Panic("number of CPU must be smaller than or equal to half the number of solutions. Ncpu=%d > Nsol/2=%d", o.Ncpu, o.Nsol/2)
	}
	if o.Tf < 1 {
		o.Tf = 1
	}
	if o.DtExc < 1 {
		o.DtExc = o.Tf / 10
	}
	if o.DtOut < 1 {
		o.DtOut = o.Tf / 5
	}

	// derived
	o.Nflt = len(o.FltMin)
	o.Nint = len(o.IntMin)
	if o.BinInt > 0 {
		o.Nint = o.BinInt
	}
	if o.Nflt == 0 && o.Nint == 0 {
		chk.Panic("either floats and ints must be set (via FltMin/Max or IntMin/Max)")
	}

	// floats
	if o.Nflt > 0 {
		chk.IntAssert(len(o.FltMax), o.Nflt)
		o.DelFlt = make([]float64, o.Nflt)
		for i := 0; i < o.Nflt; i++ {
			o.DelFlt[i] = o.FltMax[i] - o.FltMin[i]
		}
	}

	// generic ints
	if o.BinInt == 0 && o.Nint > 0 {
		chk.IntAssert(len(o.IntMax), o.Nint)
		o.DelInt = make([]int, o.Nint)
		for i := 0; i < o.Nint; i++ {
			o.DelInt[i] = o.IntMax[i] - o.IntMin[i]
		}
	}
	if o.Nint != o.Nflt {
		o.ClearFlt = false
	}

	// initialise random numbers generator
	rnd.Init(o.Seed)
}

// EnforceRange makes sure x is within given range
func (o *Parameters) EnforceRange(i int, x float64) float64 {
	if x < o.FltMin[i] {
		return o.FltMin[i]
	}
	if x > o.FltMax[i] {
		return o.FltMax[i]
	}
	return x
}

// LogParams returns a log with current parameters
func (o *Parameters) LogParams() (l string) {

	// sizes
	l += io.ArgsTable("SIZES",
		"number of objective values", "Nova", o.Nova,
		"number of out-of-range values", "Noor", o.Noor,
		"total number of solutions", "Nsol", o.Nsol,
		"number of cpus", "Ncpu", o.Ncpu,
	)

	// time
	l += "\n"
	l += io.ArgsTable("TIME",
		"final time", "Tf", o.Tf,
		"delta time for exchange", "DtExc", o.DtExc,
		"delta time for output", "DtOut", o.DtOut,
	)

	// options
	l += "\n"
	l += io.ArgsTable("OPTIONS",
		"parallel", "Pll", o.Pll,
		"seed for random numbers generator", "Seed", o.Seed,
		"generation type: 'latin', 'halton', 'rnd'", "GenType", o.GenType,
		"Latin Hypercube duplicates number", "LatinDup", o.LatinDup,
		"minimum value for 'h' constraints", "EpsH", o.EpsH,
		"show messages", "Verbose", o.Verbose,
		"generate all solutions together", "GenAll", o.GenAll,
		"run many trials", "Ntrials", o.Ntrials,
		"integers represent binary numbers", "BinInt", o.BinInt,
		"clear flt if corresponding int is 0", "ClearFlt", o.ClearFlt,
		"use exchange via tournament", "ExcTour", o.ExcTour,
		"use exchange one randomly", "ExcOne", o.ExcOne,
	)

	// crossover and mutation
	l += "\n"
	l += io.ArgsTable("CROSSOVER AND MUTATION",
		"probability of mutation for ints", "PmInt", o.PmInt,
		"probability of crossover for ints", "PcInt", o.PcInt,
	)

	// derived
	l += "\n"
	l += io.ArgsTable("DERIVED",
		"number of floats", "Nflt", o.Nflt,
		"number of integers", "Nint", o.Nint,
	)
	return
}
<|endoftext|>


<commit_before>// Copyright 2015 Igor Dolzhikov. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package router

import (
	"sort"
)

const (
	maxLevel = 255
	asterisk = "*"
)

type parser struct {
	fields   map[uint8]records
	static   map[string]Handle
	wildcard records
}

type record struct {
	key    uint16
	handle Handle
	parts  []string
}

type records []*record

func (n records) Len() int           { return len(n) }
func (n records) Swap(i, j int)      { n[i], n[j] = n[j], n[i] }
func (n records) Less(i, j int) bool { return n[i].key < n[j].key }

func newParser() *parser {
	return &parser{
		fields:   make(map[uint8]records),
		static:   make(map[string]Handle),
		wildcard: records{},
	}
}

func (p *parser) register(path string, handle Handle) bool {
	if trim(path, " ") == asterisk {
		p.static[asterisk] = handle
	}
	if parts, ok := split(path); ok {
		var static, dynamic, wildcard uint16
		for _, value := range parts {
			if len(value) >= 1 && value[0:1] == ":" {
				dynamic++
			} else if len(value) == 1 && value == "*" {
				wildcard++
			} else {
				static++
			}
		}
		if wildcard > 0 {
			p.wildcard = append(p.wildcard, &record{key: dynamic<<8 + static, handle: handle, parts: parts})
		} else if dynamic == 0 {
			p.static["/"+join(parts)] = handle
		} else {
			level := uint8(len(parts))
			p.fields[level] = append(p.fields[level], &record{key: dynamic<<8 + static, handle: handle, parts: parts})
			sort.Sort(records(p.fields[level]))
		}
		return true
	}

	return false
}

func (p *parser) get(path string) (handle Handle, result []Param, ok bool) {
	if handle, ok := p.static[asterisk]; ok {
		return handle, nil, true
	}
	if handle, ok := p.static[path]; ok {
		return handle, nil, true
	}
	if parts, ok := split(path); ok {
		if handle, ok := p.static["/"+join(parts)]; ok {
			return handle, nil, true
		}
		// try to match wildcard route
		if handle, result, ok := findHandle(p.wildcard, parts); ok {
			return handle, result, ok
		}
		if data := p.fields[uint8(len(parts))]; data != nil {
			if handle, result, ok := findHandle(data, parts); ok {
				return handle, result, ok
			}
		}
	}

	return nil, nil, false
}

func split(path string) ([]string, bool) {
	sdata := explode(trim(path, "/"))
	if len(sdata) == 0 {
		return sdata, true
	}
	var result []string
	ind := 0
	if len(sdata) < maxLevel {
		result = make([]string, len(sdata))
		for _, value := range sdata {
			if v := trim(value, " "); v == "" {
				continue
			} else {
				result[ind] = v
				ind++
			}
		}
		return result[0:ind], true
	}

	return nil, false
}

func trim(str, sep string) string {
	result := str
	for {
		if len(result) >= 1 && result[0:1] == sep {
			result = result[1:]
		} else {
			break
		}
	}
	for {
		if len(result) >= 1 && result[len(result)-1:] == sep {
			result = result[:len(result)-1]
		} else {
			break
		}
	}
	return result
}

func join(parts []string) string {
	if len(parts) == 0 {
		return ""
	}
	if len(parts) == 1 {
		return parts[0]
	}
	n := len(parts) - 1
	for i := 0; i < len(parts); i++ {
		n += len(parts[i])
	}

	b := make([]byte, n)
	bp := copy(b, parts[0])
	for _, s := range parts[1:] {
		bp += copy(b[bp:], "/")
		bp += copy(b[bp:], s)
	}
	return string(b)
}

func explode(s string) []string {
	if len(s) == 0 {
		return []string{}
	}
	n := 1
	sep := "/"
	c := sep[0]
	for i := 0; i < len(s); i++ {
		if s[i] == c {
			n++
		}
	}
	start := 0
	a := make([]string, n)
	na := 0
	for i := 0; i+1 <= len(s) && na+1 < n; i++ {
		if s[i] == c {
			a[na] = s[start:i]
			na++
			start = i + 1
		}
	}
	a[na] = s[start:]
	return a[0 : na+1]
}

func findHandle(data records, parts []string) (handle Handle, result []Param, ok bool) {
	for _, nds := range data {
		values := nds.parts
		result = nil
		found := true
		for idx, value := range values {
			if len(value) == 1 && value == "*" {
				break
			} else if value != parts[idx] && !(len(value) >= 1 && value[0:1] == ":") {
				found = false
				break
			} else {
				if len(value) >= 1 && value[0:1] == ":" {
					result = append(result, Param{Key: value, Value: parts[idx]})
				}
			}
		}
		if found {
			return nds.handle, result, true
		}
	}

	return nil, nil, false
}
<commit_msg>Code review<commit_after>// Copyright 2015 Igor Dolzhikov. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package router

import (
	"sort"
)

const (
	maxLevel = 255
	asterisk = "*"
)

type parser struct {
	fields   map[uint8]records
	static   map[string]Handle
	wildcard records
}

type record struct {
	key    uint16
	handle Handle
	parts  []string
}

type records []*record

func (n records) Len() int           { return len(n) }
func (n records) Swap(i, j int)      { n[i], n[j] = n[j], n[i] }
func (n records) Less(i, j int) bool { return n[i].key < n[j].key }

func newParser() *parser {
	return &parser{
		fields:   make(map[uint8]records),
		static:   make(map[string]Handle),
		wildcard: records{},
	}
}

func (p *parser) register(path string, handle Handle) bool {
	if trim(path, " ") == asterisk {
		p.static[asterisk] = handle
	}
	if parts, ok := split(path); ok {
		var static, dynamic, wildcard uint16
		for _, value := range parts {
			if len(value) >= 1 && value[0:1] == ":" {
				dynamic++
			} else if len(value) == 1 && value == "*" {
				wildcard++
			} else {
				static++
			}
		}
		if wildcard > 0 {
			p.wildcard = append(p.wildcard, &record{key: dynamic<<8 + static, handle: handle, parts: parts})
		} else if dynamic == 0 {
			p.static["/"+join(parts)] = handle
		} else {
			level := uint8(len(parts))
			p.fields[level] = append(p.fields[level], &record{key: dynamic<<8 + static, handle: handle, parts: parts})
			sort.Sort(records(p.fields[level]))
		}
		return true
	}

	return false
}

func (p *parser) get(path string) (handle Handle, result []Param, ok bool) {
	if handle, ok := p.static[asterisk]; ok {
		return handle, nil, true
	}
	if handle, ok := p.static[path]; ok {
		return handle, nil, true
	}
	if parts, ok := split(path); ok {
		if handle, ok := p.static["/"+join(parts)]; ok {
			return handle, nil, true
		}
		// try to match wildcard route
		if handle, result, ok := parseParams(p.wildcard, parts); ok {
			return handle, result, ok
		}
		if data := p.fields[uint8(len(parts))]; data != nil {
			if handle, result, ok := parseParams(data, parts); ok {
				return handle, result, ok
			}
		}
	}

	return nil, nil, false
}

func split(path string) ([]string, bool) {
	sdata := explode(trim(path, "/"))
	if len(sdata) == 0 {
		return sdata, true
	}
	var result []string
	ind := 0
	if len(sdata) < maxLevel {
		result = make([]string, len(sdata))
		for _, value := range sdata {
			if v := trim(value, " "); v == "" {
				continue
			} else {
				result[ind] = v
				ind++
			}
		}
		return result[0:ind], true
	}

	return nil, false
}

func trim(str, sep string) string {
	result := str
	for {
		if len(result) >= 1 && result[0:1] == sep {
			result = result[1:]
		} else {
			break
		}
	}
	for {
		if len(result) >= 1 && result[len(result)-1:] == sep {
			result = result[:len(result)-1]
		} else {
			break
		}
	}
	return result
}

func join(parts []string) string {
	if len(parts) == 0 {
		return ""
	}
	if len(parts) == 1 {
		return parts[0]
	}
	n := len(parts) - 1
	for i := 0; i < len(parts); i++ {
		n += len(parts[i])
	}

	b := make([]byte, n)
	bp := copy(b, parts[0])
	for _, s := range parts[1:] {
		bp += copy(b[bp:], "/")
		bp += copy(b[bp:], s)
	}
	return string(b)
}

func explode(s string) []string {
	if len(s) == 0 {
		return []string{}
	}
	n := 1
	sep := "/"
	c := sep[0]
	for i := 0; i < len(s); i++ {
		if s[i] == c {
			n++
		}
	}
	start := 0
	a := make([]string, n)
	na := 0
	for i := 0; i+1 <= len(s) && na+1 < n; i++ {
		if s[i] == c {
			a[na] = s[start:i]
			na++
			start = i + 1
		}
	}
	a[na] = s[start:]
	return a[0 : na+1]
}

func parseParams(data records, parts []string) (handle Handle, result []Param, ok bool) {
	for _, nds := range data {
		values := nds.parts
		result = nil
		found := true
		for idx, value := range values {
			if len(value) == 1 && value == "*" {
				break
			} else if value != parts[idx] && !(len(value) >= 1 && value[0:1] == ":") {
				found = false
				break
			} else {
				if len(value) >= 1 && value[0:1] == ":" {
					result = append(result, Param{Key: value, Value: parts[idx]})
				}
			}
		}
		if found {
			return nds.handle, result, true
		}
	}

	return nil, nil, false
}
<|endoftext|>


<commit_before>package main

import "fmt"

func main() {
	chains := make(map[int]int)
	chains[1] = 1
	chains[2] = 2
	for i := 3; i < 1000000; i++ {
		if chains[i] == 0 {
			temp := i
			chain := make([]int, 0)
			chain = append(chain, i)
			chains[i] = 1
			for {
				if temp%2 == 0 {
					temp /= 2
				} else {
					temp = 3*temp + 1
				}
				if length, ok := chains[temp]; ok {
					for k, v := range chain {
						if k == 0 {
							chains[v] = length + len(chain)
						} else {
							if v%3 == 1 {
								chains[v] = length + len(chain) - k
							}
						}
					}
					break
				} else {
					chain = append(chain, temp)
				}
			}
		} else {
			continue
		}
	}
	var max int
	var num int
	for k, v := range chains {
		if v > max {
			max = v
			num = k
		}
	}
	fmt.Println(max, num)
}
<commit_msg>put key deletion back in projecteuler 14<commit_after>package main

import "fmt"

func main() {
	chains := make(map[int]int)
	chains[1] = 1
	chains[2] = 2
	for i := 3; i < 1000000; i++ {
		if chains[i] == 0 {
			temp := i
			chain := make([]int, 0)
			chain = append(chain, i)
			chains[i] = 1
			for {
				if temp%2 == 0 {
					temp /= 2
				} else {
					temp = 3*temp + 1
				}
				if length, ok := chains[temp]; ok {
					for k, v := range chain {
						if k == 0 {
							chains[v] = length + len(chain)
						} else {
							if v%3 == 1 {
								chains[v] = length + len(chain) - k
							}
						}
					}
					delete(chains, temp)
					break
				} else {
					chain = append(chain, temp)
				}
			}
		} else {
			continue
		}
	}
	var max int
	var num int
	for k, v := range chains {
		if v > max {
			max = v
			num = k
		}
	}
	fmt.Println(max, num)
}
<|endoftext|>


<commit_before>// Copyright 2013 (c) Freek Kalter. All rights reserved.
// Use of this source code is governed by the "Revised BSD License"
// that can be found in the LICENSE file.

package main

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"log/syslog"
	"net/http"
	"os"
	"os/signal"
	"regexp"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/gorilla/mux"
	"launchpad.net/goyaml"
)

type Config struct {
	Api_key   string
	Api_url   string
	Max_speed int
	Port      int
	Times     []int
}

var slog *log.Logger
var config Config

type countDown struct {
	SetAt                  time.Time
	Duration               time.Duration
	Limit, LimitPercentage int64
}

var cDown countDown = countDown{
	SetAt:    time.Unix(0, 0),
	Duration: 0,
	Limit:    0,
}

var compiledTemplates = template.Must(template.ParseFiles("404.html"))

var sabNzbFunctions map[string]string

// ignore invalid certificates (todo: make it accecpt a valid cert)
var client = &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}

func (c countDown) ExpiresAt() (expire time.Time, err error) {
	if c.SetAt.Equal(time.Unix(0, 0)) {
		err = errors.New("timer not running")
	}
	expire = c.SetAt.Add(c.Duration)
	return
}

func (c countDown) SecondsLeft() (secs int64, err error) {
	expires, err := c.ExpiresAt()
	if err != nil {
		err = errors.New("timer not running")
	}
	secs = int64(expires.Sub(time.Now()).Seconds())
	return
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
	indexContent, err := ioutil.ReadFile("index.html")
	if err != nil {
		slog.Panic(err)
	}
	fmt.Fprintf(w, string(indexContent))
}

func resumeHandler(w http.ResponseWriter, r *http.Request) {
	cDown.Duration = 0
	cDown.Limit = 0
	callSabnzbd(sabNzbFunctions["resume_download"])
	callSabnzbd(sabNzbFunctions["reset_limit"])
}

func formHandler(w http.ResponseWriter, r *http.Request) {
	formVars := mux.Vars(r)
	valid_integer_regex := regexp.MustCompile("^[0-9]{1,3}$")
	if !valid_integer_regex.MatchString(strings.TrimSpace(formVars["time"])) ||
		!valid_integer_regex.MatchString(strings.TrimSpace(formVars["limit"])) {
		cDown.Duration = 0
		return // TODO: proper error message maybe
	}

	timer_value, _ := strconv.ParseInt(formVars["time"], 10, 32)           //base 10, 32bit integer
	cDown.LimitPercentage, _ = strconv.ParseInt(formVars["limit"], 10, 32) //base 10, 32bit integer
	cDown.Duration = time.Minute * time.Duration(timer_value)
	cDown.Limit = int64(config.Max_speed) - ((int64(config.Max_speed) / 100) * cDown.LimitPercentage) // percentage give is how much to block, so inverse that to get how much to let through
	cDown.SetAt = time.Now()
	slog.Printf("timer started: %+v\n", cDown)
	time.AfterFunc(cDown.Duration, func() {
		slog.Printf("timer done: %+v\n", cDown)
		cDown.Duration = 0
		cDown.SetAt = time.Unix(0, 0)
		callSabnzbd(sabNzbFunctions["resume_download"])
		callSabnzbd(sabNzbFunctions["reset_limit"])
	})

	if cDown.LimitPercentage == 100 {
		go callSabnzbd(fmt.Sprintf(sabNzbFunctions["pause"], timer_value))
	} else {
		go callSabnzbd(fmt.Sprintf(sabNzbFunctions["limit"], cDown.Limit))
	}
}

func currentStateHandler(w http.ResponseWriter, r *http.Request) {
	var limit, dur int64
	secs, err := cDown.SecondsLeft()
	if err != nil || secs <= 0 {
		limit, dur = 0, 0
	} else {
		dur = int64(cDown.Duration.Minutes())
		limit = cDown.LimitPercentage
	}
    state := map[string]interface{}{"secondsLeft": secs, "limit": limit, "time": dur, "times": config.Times}

	jsonEncoder := json.NewEncoder(w)
	jsonEncoder.Encode(state)
}

func notFound(w http.ResponseWriter, r *http.Request) {
	err := compiledTemplates.ExecuteTemplate(w, "404.html", r.URL)
	if err != nil {
		slog.Panic(err)
	}
}

func callSabnzbd(url string) {
	resp, err := client.Get(url)
	if err != nil {
		slog.Panic(err)
	}
	defer resp.Body.Close()
}

func initSabnzbFunctions() {
	sabNzbFunctions = map[string]string{
		"reset_limit":     fmt.Sprintf("%sapi?mode=config&name=speedlimit&value=0&apikey=%v", config.Api_url, config.Api_key),
		"resume_download": fmt.Sprintf("%vapi?mode=resume&apikey=%v", config.Api_url, config.Api_key),
		"pause":           fmt.Sprintf("%vapi?mode=config&name=set_pause&value=%%v&apikey=%v", config.Api_url, config.Api_key),
		"limit":           fmt.Sprintf("%vapi?mode=config&name=speedlimit&value=%%v&apikey=%v", config.Api_url, config.Api_key),
	}
}

func cacheHandler(dur time.Duration, h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Cache-Control", fmt.Sprintf("max-age=%d, public, must-revalidate, proxy-revalidate", int64(dur.Seconds())))
		h.ServeHTTP(w, r)
	})
}

func main() {
	// Set up logging
	var err error
	slog, err = syslog.NewLogger(syslog.LOG_NOTICE|syslog.LOG_USER, 0)
	if err != nil {
		slog.Panic(err)
	}
	slog.SetPrefix("pauzer: ")

	// Set up gracefull termination
	killChannel := make(chan os.Signal, 1)
	signal.Notify(killChannel, os.Interrupt, os.Kill, syscall.SIGTERM)
	go func(c chan os.Signal, l *log.Logger) {
		<-c
		l.Println("shutting down")
		os.Exit(0)
	}(killChannel, slog)

	// Load config
	configFile, err := ioutil.ReadFile("config.yml")
	if err != nil {
		slog.Panic(err)
	}
	err = goyaml.Unmarshal(configFile, &config)
	if err != nil {
		slog.Panic(err)
	}
	initSabnzbFunctions()

	// set up gorilla/mux handlers
	r := mux.NewRouter()
	r.HandleFunc("/", homeHandler)
	r.HandleFunc("/action/{time:[0-9]+}/{limit:[0-9]+}", formHandler)
	r.HandleFunc("/resume", resumeHandler)
	r.HandleFunc("/state", currentStateHandler)

	// static files get served directly
	r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", cacheHandler(time.Second*2678400, http.FileServer(http.Dir("js/")))))
	r.PathPrefix("/img/").Handler(http.StripPrefix("/img/", cacheHandler(time.Second*2678400, http.FileServer(http.Dir("img/")))))
	r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", cacheHandler(time.Second*2678400,http.FileServer(http.Dir("css/")))))
	r.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Cache-Control", fmt.Sprintf("max-age=%d, public, must-revalidate, proxy-revalidate", int64(time.Second*2678400)))
		http.ServeFile(w, r, "favicon.ico")
	})
	r.NotFoundHandler = http.HandlerFunc(notFound)

	http.Handle("/", r)
	slog.Println("started on port", config.Port)
	http.ListenAndServe(":4000", r)
}
<commit_msg>return to state before pausing<commit_after>// Copyright 2013 (c) Freek Kalter. All rights reserved.
// Use of this source code is governed by the "Revised BSD License"
// that can be found in the LICENSE file.

package main

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"log/syslog"
	"net/http"
	"os"
	"os/signal"
	"regexp"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/gorilla/mux"
	"launchpad.net/goyaml"
)

type Config struct {
	Api_key   string
	Api_url   string
	Max_speed int
	Port      int
	Times     []int
}

var slog *log.Logger
var config Config

type countDown struct {
	SetAt                  time.Time
	Duration               time.Duration
	Limit, LimitPercentage int64
	ReturnState            ReturnState
}
type ReturnState struct {
	Speedlimit string
	Paused     bool
}

var cDown countDown = countDown{
	SetAt:    time.Unix(0, 0),
	Duration: 0,
	Limit:    0,
}

var compiledTemplates = template.Must(template.ParseFiles("404.html"))

var sabNzbFunctions map[string]string

// ignore invalid certificates (todo: make it accecpt a valid cert)
var client = &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}

func (c countDown) ExpiresAt() (expire time.Time, err error) {
	if c.SetAt.Equal(time.Unix(0, 0)) {
		err = errors.New("timer not running")
	}
	expire = c.SetAt.Add(c.Duration)
	return
}

func (c countDown) SecondsLeft() (secs int64, err error) {
	expires, err := c.ExpiresAt()
	if err != nil {
		err = errors.New("timer not running")
	}
	secs = int64(expires.Sub(time.Now()).Seconds())
	return
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
	indexContent, err := ioutil.ReadFile("index.html")
	if err != nil {
		slog.Panic(err)
	}
	fmt.Fprintf(w, string(indexContent))
}

func resumeHandler(w http.ResponseWriter, r *http.Request) {
	cDown.Duration = 0
	cDown.Limit = 0
    resumeDownload(cDown.ReturnState)
}

func formHandler(w http.ResponseWriter, r *http.Request) {
	formVars := mux.Vars(r)
	valid_integer_regex := regexp.MustCompile("^[0-9]{1,3}$")
	if !valid_integer_regex.MatchString(strings.TrimSpace(formVars["time"])) ||
		!valid_integer_regex.MatchString(strings.TrimSpace(formVars["limit"])) {
		cDown.Duration = 0
		return // TODO: proper error message maybe
	}

	timer_value, _ := strconv.ParseInt(formVars["time"], 10, 32)           //base 10, 32bit integer
	cDown.LimitPercentage, _ = strconv.ParseInt(formVars["limit"], 10, 32) //base 10, 32bit integer
	cDown.Duration = time.Minute * time.Duration(timer_value)
	cDown.Limit = int64(config.Max_speed) - ((int64(config.Max_speed) / 100) * cDown.LimitPercentage) // percentage give is how much to block, so inverse that to get how much to let through
	cDown.SetAt = time.Now()
	cDown.ReturnState = getCurrentState()
	slog.Printf("timer started: %+v\n", cDown)
	time.AfterFunc(cDown.Duration, func() {
		slog.Printf("timer done: %+v\n", cDown)
		cDown.Duration = 0
		cDown.SetAt = time.Unix(0, 0)
        resumeDownload( cDown.ReturnState)
	})

	if cDown.LimitPercentage == 100 {
		go callSabnzbd(fmt.Sprintf(sabNzbFunctions["pause_time"], timer_value))
	} else {
		go callSabnzbd(fmt.Sprintf(sabNzbFunctions["limit"], cDown.Limit))
	}

}

func resumeDownload(cs ReturnState){
    if cs.Paused {
        go callSabnzbd( sabNzbFunctions["pause"] )
    }else{
        go callSabnzbd( sabNzbFunctions["resume_download"] )
    }
    go callSabnzbd(fmt.Sprintf( sabNzbFunctions["limit"], cs.Speedlimit))
}

func getCurrentState() ReturnState {
	resp, err := client.Get(sabNzbFunctions["status"])
	if err != nil {
		slog.Panic(err)
	}
	defer resp.Body.Close()

	text, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	type Queue struct {
		Queue ReturnState
	}
	var q Queue
	err = json.Unmarshal(text, &q)
	if err != nil {
		panic(err)
	}
	return q.Queue
}

func currentStateHandler(w http.ResponseWriter, r *http.Request) {
	var limit, dur int64
	secs, err := cDown.SecondsLeft()
	if err != nil || secs <= 0 {
		limit, dur = 0, 0
	} else {
		dur = int64(cDown.Duration.Minutes())
		limit = cDown.LimitPercentage
	}
	state := map[string]interface{}{"secondsLeft": secs, "limit": limit, "time": dur, "times": config.Times}

	jsonEncoder := json.NewEncoder(w)
	jsonEncoder.Encode(state)
}

func notFound(w http.ResponseWriter, r *http.Request) {
	err := compiledTemplates.ExecuteTemplate(w, "404.html", r.URL)
	if err != nil {
		slog.Panic(err)
	}
}

func callSabnzbd(url string) {
	resp, err := client.Get(url)
	if err != nil {
		slog.Panic(err)
	}
	defer resp.Body.Close()
}

func initSabnzbFunctions() {
	sabNzbFunctions = map[string]string{
		"reset_limit":     fmt.Sprintf("%sapi?mode=config&name=speedlimit&value=0&apikey=%v", config.Api_url, config.Api_key),
		"resume_download": fmt.Sprintf("%vapi?mode=resume&apikey=%v", config.Api_url, config.Api_key),
		"pause_time":           fmt.Sprintf("%vapi?mode=config&name=set_pause&value=%%v&apikey=%v", config.Api_url, config.Api_key),
		"pause":           fmt.Sprintf("%vapi?mode=pause&apikey=%v", config.Api_url, config.Api_key),
		"limit":           fmt.Sprintf("%vapi?mode=config&name=speedlimit&value=%%v&apikey=%v", config.Api_url, config.Api_key),
		"status":          fmt.Sprintf("%vapi?mode=queue&start=START&limit=LIMIT&apikey=%v&output=json", config.Api_url, config.Api_key),
	}
}

func cacheHandler(dur time.Duration, h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Cache-Control", fmt.Sprintf("max-age=%d, public, must-revalidate, proxy-revalidate", int64(dur.Seconds())))
		h.ServeHTTP(w, r)
	})
}

func main() {
	// Set up logging
	var err error
	slog, err = syslog.NewLogger(syslog.LOG_NOTICE|syslog.LOG_USER, 0)
	if err != nil {
		slog.Panic(err)
	}
	slog.SetPrefix("pauzer: ")

	// Set up gracefull termination
	killChannel := make(chan os.Signal, 1)
	signal.Notify(killChannel, os.Interrupt, os.Kill, syscall.SIGTERM)
	go func(c chan os.Signal, l *log.Logger) {
		<-c
		l.Println("shutting down")
		os.Exit(0)
	}(killChannel, slog)

	// Load config
	configFile, err := ioutil.ReadFile("config.yml")
	if err != nil {
		slog.Panic(err)
	}
	err = goyaml.Unmarshal(configFile, &config)
	if err != nil {
		slog.Panic(err)
	}
	initSabnzbFunctions()

	getCurrentState()
	// set up gorilla/mux handlers
	r := mux.NewRouter()
	r.HandleFunc("/", homeHandler)
	r.HandleFunc("/action/{time:[0-9]+}/{limit:[0-9]+}", formHandler)
	r.HandleFunc("/resume", resumeHandler)
	r.HandleFunc("/state", currentStateHandler)

	// static files get served directly
	r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", cacheHandler(time.Second*2678400, http.FileServer(http.Dir("js/")))))
	r.PathPrefix("/img/").Handler(http.StripPrefix("/img/", cacheHandler(time.Second*2678400, http.FileServer(http.Dir("img/")))))
	r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", cacheHandler(time.Second*2678400, http.FileServer(http.Dir("css/")))))
	r.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Cache-Control", fmt.Sprintf("max-age=%d, public, must-revalidate, proxy-revalidate", int64(time.Second*2678400)))
		http.ServeFile(w, r, "favicon.ico")
	})
	r.NotFoundHandler = http.HandlerFunc(notFound)

	http.Handle("/", r)
	slog.Println("started on port", config.Port)
	http.ListenAndServe(":4000", r)
}
<|endoftext|>