1// Copyright 2018 The CUE Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package list
16
17import (
18	"fmt"
19
20	"github.com/cockroachdb/apd/v2"
21
22	"cuelang.org/go/internal"
23)
24
25// Avg returns the average value of a non empty list xs.
26func Avg(xs []*internal.Decimal) (*internal.Decimal, error) {
27	if 0 == len(xs) {
28		return nil, fmt.Errorf("empty list")
29	}
30
31	s := apd.New(0, 0)
32	for _, x := range xs {
33		_, err := internal.BaseContext.Add(s, x, s)
34		if err != nil {
35			return nil, err
36		}
37	}
38
39	var d apd.Decimal
40	l := apd.New(int64(len(xs)), 0)
41	_, err := internal.BaseContext.Quo(&d, s, l)
42	if err != nil {
43		return nil, err
44	}
45	return &d, nil
46}
47
48// Max returns the maximum value of a non empty list xs.
49func Max(xs []*internal.Decimal) (*internal.Decimal, error) {
50	if 0 == len(xs) {
51		return nil, fmt.Errorf("empty list")
52	}
53
54	max := xs[0]
55	for _, x := range xs[1:] {
56		if -1 == max.Cmp(x) {
57			max = x
58		}
59	}
60	return max, nil
61}
62
63// Min returns the minimum value of a non empty list xs.
64func Min(xs []*internal.Decimal) (*internal.Decimal, error) {
65	if 0 == len(xs) {
66		return nil, fmt.Errorf("empty list")
67	}
68
69	min := xs[0]
70	for _, x := range xs[1:] {
71		if +1 == min.Cmp(x) {
72			min = x
73		}
74	}
75	return min, nil
76}
77
78// Product returns the product of a non empty list xs.
79func Product(xs []*internal.Decimal) (*internal.Decimal, error) {
80	d := apd.New(1, 0)
81	for _, x := range xs {
82		_, err := internal.BaseContext.Mul(d, x, d)
83		if err != nil {
84			return nil, err
85		}
86	}
87	return d, nil
88}
89
90// Range generates a list of numbers using a start value, a limit value, and a
91// step value.
92//
93// For instance:
94//
95//    Range(0, 5, 2)
96//
97// results in
98//
99//    [0, 2, 4]
100//
101func Range(start, limit, step *internal.Decimal) ([]*internal.Decimal, error) {
102	if step.IsZero() {
103		return nil, fmt.Errorf("step must be non zero")
104	}
105
106	if !step.Negative && +1 == start.Cmp(limit) {
107		return nil, fmt.Errorf("end must be greater than start when step is positive")
108	}
109
110	if step.Negative && -1 == start.Cmp(limit) {
111		return nil, fmt.Errorf("end must be less than start when step is negative")
112	}
113
114	var vals []*internal.Decimal
115	num := start
116	for {
117		if !step.Negative && -1 != num.Cmp(limit) {
118			break
119		}
120
121		if step.Negative && +1 != num.Cmp(limit) {
122			break
123		}
124
125		vals = append(vals, num)
126		d := apd.New(0, 0)
127		_, err := internal.BaseContext.Add(d, step, num)
128		if err != nil {
129			return nil, err
130		}
131		num = d
132	}
133	return vals, nil
134}
135
136// Sum returns the sum of a list non empty xs.
137func Sum(xs []*internal.Decimal) (*internal.Decimal, error) {
138	d := apd.New(0, 0)
139	for _, x := range xs {
140		_, err := internal.BaseContext.Add(d, x, d)
141		if err != nil {
142			return nil, err
143		}
144	}
145	return d, nil
146}
147