package eval

import (
	
	
	
	
	
	

	
	
	
	
)

// Sequence, list and maps.

func () {
	addBuiltinFns(map[string]interface{}{
		"ns": nsFn,

		"make-map": makeMap,

		"range":  rangeFn,
		"repeat": repeat,

		"assoc":  assoc,
		"dissoc": dissoc,

		"all": all,
		"one": one,

		"has-key":   hasKey,
		"has-value": hasValue,

		"take":  take,
		"drop":  drop,
		"count": count,

		"keys": keys,

		"order": order,
	})
}

//elvdoc:fn ns
//
// ```elvish
// ns $map
// ```
//
// Constructs a namespace from `$map`, using the keys as variable names and the
// values as their values. Examples:
//
// ```elvish-transcript
// ~> n = (ns [&name=value])
// ~> put $n[name]
// ▶ value
// ~> n: = (ns [&name=value])
// ~> put $n:name
// ▶ value
// ```

func ( hashmap.Map) (*Ns, error) {
	 := make(NsBuilder)
	for  := .Iterator(); .HasElem(); .Next() {
		,  := .Elem()
		,  := .(string)
		if ! {
			return nil, errs.BadValue{
				What:  `key of argument of "ns"`,
				Valid: "string", Actual: vals.Kind()}
		}
		[] = vars.FromInit()
	}
	return .Ns(), nil
}

//elvdoc:fn make-map
//
// ```elvish
// make-map $input?
// ```
//
// Outputs a map from an input consisting of containers with two elements. The
// first element of each container is used as the key, and the second element is
// used as the value.
//
// If the same key appears multiple times, the last value is used.
//
// Examples:
//
// ```elvish-transcript
// ~> make-map [[k v]]
// ▶ [&k=v]
// ~> make-map [[k v1] [k v2]]
// ▶ [&k=v2]
// ~> put [k1 v1] [k2 v2] | make-map
// ▶ [&k1=v1 &k2=v2]
// ~> put aA bB | make-map
// ▶ [&a=A &b=B]
// ```

func ( Inputs) (vals.Map, error) {
	 := vals.EmptyMap
	var  error
	(func( interface{}) {
		if  != nil {
			return
		}
		if !vals.CanIterate() {
			 = errs.BadValue{
				What: "input to make-map", Valid: "iterable", Actual: vals.Kind()}
			return
		}
		if  := vals.Len();  != 2 {
			 = errs.BadValue{
				What: "input to make-map", Valid: "iterable with 2 elements",
				Actual: fmt.Sprintf("%v with %v elements", vals.Kind(), )}
			return
		}
		,  := vals.Collect()
		if  != nil {
			 = 
			return
		}
		if len() != 2 {
			 = fmt.Errorf("internal bug: collected %v values", len())
			return
		}
		 = .Assoc([0], [1])
	})
	return , 
}

//elvdoc:fn range
//
// ```elvish
// range &step=1 $low? $high
// ```
//
// Output `$low`, `$low` + `$step`, ..., proceeding as long as smaller than
// `$high` or until overflow. If not given, `$low` defaults to 0. The `$step`
// must be positive.
//
// This command is [exactness-preserving](#exactness-preserving).
//
// Examples:
//
// ```elvish-transcript
// ~> range 4
// ▶ 0
// ▶ 1
// ▶ 2
// ▶ 3
// ~> range 1 6 &step=2
// ▶ 1
// ▶ 3
// ▶ 5
// ```
//
// When using floating-point numbers, beware that numerical errors can result in
// an incorrect number of outputs:
//
// ```elvish-transcript
// ~> range 0.9 &step=0.3
// ▶ (num 0.0)
// ▶ (num 0.3)
// ▶ (num 0.6)
// ▶ (num 0.8999999999999999)
// ```
//
// Avoid this problem by using exact rationals:
//
// ```elvish-transcript
// ~> range 9/10 &step=3/10
// ▶ (num 0)
// ▶ (num 3/10)
// ▶ (num 3/5)
// ```
//
// Etymology:
// [Python](https://docs.python.org/3/library/functions.html#func-range).

type rangeOpts struct{ Step vals.Num }

func ( *rangeOpts) () { .Step = 1 }

func ( *Frame,  rangeOpts,  ...vals.Num) error {
	var  []vals.Num
	switch len() {
	case 1:
		 = []vals.Num{0, [0], .Step}
	case 2:
		 = []vals.Num{[0], [1], .Step}
	default:
		return ErrArgs
	}
	switch step := .Step.(type) {
	case int:
		if  <= 0 {
			return errs.BadValue{
				What: "step", Valid: "positive", Actual: strconv.Itoa()}
		}
	case *big.Int:
		if .Sign() <= 0 {
			return errs.BadValue{
				What: "step", Valid: "positive", Actual: .String()}
		}
	case *big.Rat:
		if .Sign() <= 0 {
			return errs.BadValue{
				What: "step", Valid: "positive", Actual: .String()}
		}
	case float64:
		if  <= 0 {
			return errs.BadValue{
				What: "step", Valid: "positive", Actual: vals.ToString()}
		}
	}
	 := vals.UnifyNums(, vals.Int)

	 := .OutputChan()
	switch nums := .(type) {
	case []int:
		, ,  := [0], [1], [2]
		for  := ;  < ;  +=  {
			 <- vals.FromGo()
			if + <=  {
				// Overflow
				break
			}
		}
	case []*big.Int:
		, ,  := [0], [1], [2]
		 := &big.Int{}
		for .Set(); .Cmp() < 0; {
			 <- vals.FromGo()
			 := &big.Int{}
			.Add(, )
			 = 
		}
	case []*big.Rat:
		, ,  := [0], [1], [2]
		 := &big.Rat{}
		for .Set(); .Cmp() < 0; {
			 <- vals.FromGo()
			 := &big.Rat{}
			.Add(, )
			 = 
		}
	case []float64:
		, ,  := [0], [1], [2]
		for  := ;  < ;  +=  {
			 <- vals.FromGo()
			if + <=  {
				// Overflow
				break
			}
		}
	default:
		panic("unreachable")
	}

	return nil
}

//elvdoc:fn repeat
//
// ```elvish
// repeat $n $value
// ```
//
// Output `$value` for `$n` times. Example:
//
// ```elvish-transcript
// ~> repeat 0 lorem
// ~> repeat 4 NAN
// ▶ NAN
// ▶ NAN
// ▶ NAN
// ▶ NAN
// ```
//
// Etymology: [Clojure](https://clojuredocs.org/clojure.core/repeat).

func ( *Frame,  int,  interface{}) {
	 := .OutputChan()
	for  := 0;  < ; ++ {
		 <- 
	}
}

//elvdoc:fn assoc
//
// ```elvish
// assoc $container $k $v
// ```
//
// Output a slightly modified version of `$container`, such that its value at `$k`
// is `$v`. Applies to both lists and to maps.
//
// When `$container` is a list, `$k` may be a negative index. However, slice is not
// yet supported.
//
// ```elvish-transcript
// ~> assoc [foo bar quux] 0 lorem
// ▶ [lorem bar quux]
// ~> assoc [foo bar quux] -1 ipsum
// ▶ [foo bar ipsum]
// ~> assoc [&k=v] k v2
// ▶ [&k=v2]
// ~> assoc [&k=v] k2 v2
// ▶ [&k2=v2 &k=v]
// ```
//
// Etymology: [Clojure](https://clojuredocs.org/clojure.core/assoc).
//
// @cf dissoc

func (, ,  interface{}) (interface{}, error) {
	return vals.Assoc(, , )
}

var errCannotDissoc = errors.New("cannot dissoc")

//elvdoc:fn dissoc
//
// ```elvish
// dissoc $map $k
// ```
//
// Output a slightly modified version of `$map`, with the key `$k` removed. If
// `$map` does not contain `$k` as a key, the same map is returned.
//
// ```elvish-transcript
// ~> dissoc [&foo=bar &lorem=ipsum] foo
// ▶ [&lorem=ipsum]
// ~> dissoc [&foo=bar &lorem=ipsum] k
// ▶ [&lorem=ipsum &foo=bar]
// ```
//
// @cf assoc

func (,  interface{}) (interface{}, error) {
	 := vals.Dissoc(, )
	if  == nil {
		return nil, errCannotDissoc
	}
	return , nil
}

//elvdoc:fn all
//
// ```elvish
// all $input-list?
// ```
//
// Passes inputs to the output as is. Byte inputs into values, one per line.
//
// This is an identity function for commands with value outputs: `a | all` is
// equivalent to `a` if it only outputs values.
//
// This function is useful for turning inputs into arguments, like:
//
// ```elvish-transcript
// ~> use str
// ~> put 'lorem,ipsum' | str:split , (all)
// ▶ lorem
// ▶ ipsum
// ```
//
// Or capturing all inputs in a variable:
//
// ```elvish-transcript
// ~> x = [(all)]
// foo
// bar
// (Press ^D)
// ~> put $x
// ▶ [foo bar]
// ```
//
// When given a list, it outputs all elements of the list:
//
// ```elvish-transcript
// ~> all [foo bar]
// ▶ foo
// ▶ bar
// ```
//
// @cf one

func ( *Frame,  Inputs) {
	 := .OutputChan()
	(func( interface{}) {  <-  })
}

//elvdoc:fn one
//
// ```elvish
// one $input-list?
// ```
//
// Passes inputs to outputs, if there is only a single one. Otherwise raises an
// exception.
//
// This function can be used in a similar way to [`all`](#all), but is a better
// choice when you expect that there is exactly one output:
//
// @cf all

func ( *Frame,  Inputs) error {
	var  interface{}
	 := 0
	(func( interface{}) {
		if  == 0 {
			 = 
		}
		++
	})
	if  == 1 {
		.OutputChan() <- 
		return nil
	}
	return fmt.Errorf("expect a single value, got %d", )
}

//elvdoc:fn take
//
// ```elvish
// take $n $input-list?
// ```
//
// Retain the first `$n` input elements. If `$n` is larger than the number of input
// elements, the entire input is retained. Examples:
//
// ```elvish-transcript
// ~> take 3 [a b c d e]
// ▶ a
// ▶ b
// ▶ c
// ~> use str
// ~> str:split ' ' 'how are you?' | take 1
// ▶ how
// ~> range 2 | take 10
// ▶ 0
// ▶ 1
// ```
//
// Etymology: Haskell.

func ( *Frame,  int,  Inputs) {
	 := .OutputChan()
	 := 0
	(func( interface{}) {
		if  <  {
			 <- 
		}
		++
	})
}

//elvdoc:fn drop
//
// ```elvish
// drop $n $input-list?
// ```
//
// Drop the first `$n` elements of the input. If `$n` is larger than the number of
// input elements, the entire input is dropped.
//
// Example:
//
// ```elvish-transcript
// ~> drop 2 [a b c d e]
// ▶ c
// ▶ d
// ▶ e
// ~> use str
// ~> str:split ' ' 'how are you?' | drop 1
// ▶ are
// ▶ 'you?'
// ~> range 2 | drop 10
// ```
//
// Etymology: Haskell.
//
// @cf take

func ( *Frame,  int,  Inputs) {
	 := .OutputChan()
	 := 0
	(func( interface{}) {
		if  >=  {
			 <- 
		}
		++
	})
}

//elvdoc:fn has-value
//
// ```elvish
// has-value $container $value
// ```
//
// Determine whether `$value` is a value in `$container`.
//
// Examples, maps:
//
// ```elvish-transcript
// ~> has-value [&k1=v1 &k2=v2] v1
// ▶ $true
// ~> has-value [&k1=v1 &k2=v2] k1
// ▶ $false
// ```
//
// Examples, lists:
//
// ```elvish-transcript
// ~> has-value [v1 v2] v1
// ▶ $true
// ~> has-value [v1 v2] k1
// ▶ $false
// ```
//
// Examples, strings:
//
// ```elvish-transcript
// ~> has-value ab b
// ▶ $true
// ~> has-value ab c
// ▶ $false
// ```

func (,  interface{}) (bool, error) {
	switch container := .(type) {
	case hashmap.Map:
		for  := .Iterator(); .HasElem(); .Next() {
			,  := .Elem()
			if vals.Equal(, ) {
				return true, nil
			}
		}
		return false, nil
	default:
		var  bool
		 := vals.Iterate(, func( interface{}) bool {
			 = ( == )
			return !
		})
		return , 
	}
}

//elvdoc:fn has-key
//
// ```elvish
// has-key $container $key
// ```
//
// Determine whether `$key` is a key in `$container`. A key could be a map key or
// an index on a list or string. This includes a range of indexes.
//
// Examples, maps:
//
// ```elvish-transcript
// ~> has-key [&k1=v1 &k2=v2] k1
// ▶ $true
// ~> has-key [&k1=v1 &k2=v2] v1
// ▶ $false
// ```
//
// Examples, lists:
//
// ```elvish-transcript
// ~> has-key [v1 v2] 0
// ▶ $true
// ~> has-key [v1 v2] 1
// ▶ $true
// ~> has-key [v1 v2] 2
// ▶ $false
// ~> has-key [v1 v2] 0:2
// ▶ $true
// ~> has-key [v1 v2] 0:3
// ▶ $false
// ```
//
// Examples, strings:
//
// ```elvish-transcript
// ~> has-key ab 0
// ▶ $true
// ~> has-key ab 1
// ▶ $true
// ~> has-key ab 2
// ▶ $false
// ~> has-key ab 0:2
// ▶ $true
// ~> has-key ab 0:3
// ▶ $false
// ```

func (,  interface{}) bool {
	return vals.HasKey(, )
}

//elvdoc:fn count
//
// ```elvish
// count $input-list?
// ```
//
// Count the number of inputs.
//
// Examples:
//
// ```elvish-transcript
// ~> count lorem # count bytes in a string
// ▶ 5
// ~> count [lorem ipsum]
// ▶ 2
// ~> range 100 | count
// ▶ 100
// ~> seq 100 | count
// ▶ 100
// ```

// The count implementation uses a custom varargs based implementation rather
// than the more common `Inputs` API (see pkg/eval/go_fn.go) because this
// allows the implementation to be O(1) for the common cases rather than O(n).
func ( *Frame,  ...interface{}) (int, error) {
	var  int
	switch  := len();  {
	case 0:
		// Count inputs.
		.IterateInputs(func(interface{}) {
			++
		})
	case 1:
		// Get length of argument.
		 := [0]
		if  := vals.Len();  >= 0 {
			 = 
		} else {
			 := vals.Iterate(, func(interface{}) bool {
				++
				return true
			})
			if  != nil {
				return 0, fmt.Errorf("cannot get length of a %s", vals.Kind())
			}
		}
	default:
		// The error matches what would be returned if the `Inputs` API was
		// used. See GoFn.Call().
		return 0, errs.ArityMismatch{
			What: "arguments here", ValidLow: 0, ValidHigh: 1, Actual: }
	}
	return , nil
}

//elvdoc:fn keys
//
// ```elvish
// keys $map
// ```
//
// Put all keys of `$map` on the structured stdout.
//
// Example:
//
// ```elvish-transcript
// ~> keys [&a=foo &b=bar &c=baz]
// ▶ a
// ▶ c
// ▶ b
// ```
//
// Note that there is no guaranteed order for the keys of a map.

func ( *Frame,  interface{}) error {
	 := .OutputChan()
	return vals.IterateKeys(, func( interface{}) bool {
		 <- 
		return true
	})
}

//elvdoc:fn order
//
// ```elvish
// order &reverse=$false $less-than=$nil $inputs?
// ```
//
// Outputs the input values sorted in ascending order. The sort is guaranteed to
// be [stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability).
//
// The `&reverse` option, if true, reverses the order of output.
//
// The `&less-than` option, if given, establishes the ordering of the elements.
// Its value should be a function that takes two arguments and outputs a single
// boolean indicating whether the first argument is less than the second
// argument. If the function throws an exception, `order` rethrows the exception
// without outputting any value.
//
// If `&less-than` has value `$nil` (the default if not set), the following
// comparison algorithm is used:
//
// - Numbers are compared numerically. For the sake of sorting, `NaN` is treated
//   as smaller than all other numbers.
//
// - Strings are compared lexicographically by bytes, which is equivalent to
//   comparing by codepoints under UTF-8.
//
// - Lists are compared lexicographically by elements, if the elements at the
//   same positions are comparable.
//
// If the ordering between two elements are not defined by the conditions above,
// no value is outputted and an exception is thrown.
//
// Examples:
//
// ```elvish-transcript
// ~> put foo bar ipsum | order
// ▶ bar
// ▶ foo
// ▶ ipsum
// ~> order [(float64 10) (float64 1) (float64 5)]
// ▶ (float64 1)
// ▶ (float64 5)
// ▶ (float64 10)
// ~> order [[a b] [a] [b b] [a c]]
// ▶ [a]
// ▶ [a b]
// ▶ [a c]
// ▶ [b b]
// ~> order &reverse [a c b]
// ▶ c
// ▶ b
// ▶ a
// ~> order &less-than=[a b]{ eq $a x } [l x o r x e x m]
// ▶ x
// ▶ x
// ▶ x
// ▶ l
// ▶ o
// ▶ r
// ▶ e
// ▶ m
// ```
//
// Beware that strings that look like numbers are treated as strings, not
// numbers. To sort strings as numbers, use an explicit `&less-than` option:
//
// ```elvish-transcript
// ~> order [5 1 10]
// ▶ 1
// ▶ 10
// ▶ 5
// ~> order &less-than=[a b]{ < $a $b } [5 1 10]
// ▶ 1
// ▶ 5
// ▶ 10
// ```

type orderOptions struct {
	Reverse  bool
	LessThan Callable
}

func ( *orderOptions) () {}

// ErrUncomparable is raised by the order command when inputs contain
// uncomparable values.
var ErrUncomparable = errs.BadValue{
	What:  `inputs to "order"`,
	Valid: "comparable values", Actual: "uncomparable values"}

func ( *Frame,  orderOptions,  Inputs) error {
	var  []interface{}
	(func( interface{}) {  = append(, ) })

	var  error
	var  func(,  int) bool
	if .LessThan != nil {
		 = func(,  int) bool {
			if  != nil {
				return true
			}
			var  []interface{}
			if .Reverse {
				 = []interface{}{[], []}
			} else {
				 = []interface{}{[], []}
			}
			,  := .CaptureOutput(func( *Frame) error {
				return .LessThan.Call(, , NoOpts)
			})
			if  != nil {
				 = 
				return true
			}
			if len() != 1 {
				 = errs.BadValue{
					What:   "output of the &less-than callback",
					Valid:  "a single boolean",
					Actual: fmt.Sprintf("%d values", len())}
				return true
			}
			if ,  := [0].(bool);  {
				return 
			}
			 = errs.BadValue{
				What:  "output of the &less-than callback",
				Valid: "boolean", Actual: vals.Kind([0])}
			return true
		}
	} else {
		// Use default comparison implemented by compare.
		 = func(,  int) bool {
			if  != nil {
				return true
			}
			 := compare([], [])
			if  == uncomparable {
				 = ErrUncomparable
				return true
			}
			if .Reverse {
				return  == more
			}
			return  == less
		}
	}

	sort.SliceStable(, )

	if  != nil {
		return 
	}
	for ,  := range  {
		.OutputChan() <- 
	}
	return nil
}

type ordering uint8

const (
	less ordering = iota
	equal
	more
	uncomparable
)

func (,  interface{}) ordering {
	switch a := .(type) {
	case float64:
		if ,  := .(float64);  {
			switch {
			case math.IsNaN():
				if math.IsNaN() {
					return equal
				}
				return less
			case math.IsNaN():
				return more
			case  == :
				return equal
			case  < :
				return less
			default:
				// a > b
				return more
			}
		}
	case string:
		if ,  := .(string);  {
			switch {
			case  == :
				return equal
			case  < :
				return less
			default:
				// a > b
				return more
			}
		}
	case vals.List:
		if ,  := .(vals.List);  {
			 := .Iterator()
			 := .Iterator()
			for .HasElem() && .HasElem() {
				 := (.Elem(), .Elem())
				if  != equal {
					return 
				}
				.Next()
				.Next()
			}
			switch {
			case .Len() == .Len():
				return equal
			case .Len() < .Len():
				return less
			default:
				// a.Len() > b.Len()
				return more
			}
		}
	}
	return uncomparable
}