store

package
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 6, 2024 License: Apache-2.0 Imports: 6 Imported by: 0

README

Store

Deprecated: This package is deprecated and will be removed in the next major release.

Package store provides a modeling kit for decision automation problems. It is based on the paradigm of "decisions as code". The base interface is the Store: a space defined by variables and logic. The underlying algorithms search that space and find the best solution possible, this is, the best collection of variable assignments. The Store is the root node of a search tree. Child Stores (nodes) inherit both logic and variables from the parent and may also add new variables and logic, or overwrite existing ones. Changes to a child do not impact its parent.

See godocs for package docs.

Documentation

Overview

Package store provides a modeling kit for decision automation problems. It is based on the paradigm of "decisions as code". The base interface is the Store: a space defined by variables and logic. The underlying algorithms search that space and find the best solution possible, this is, the best collection of variable assignments. The Store is the root node of a search tree. Child Stores (nodes) inherit both logic and variables from the parent and may also add new variables and logic, or overwrite existing ones. Changes to a child do not impact its parent.

A new Store is defined.

s := store.New()

Variables are stored in the Store.

x := store.NewVar(s, 1)
y := store.NewSlice(s, 2, 3, 4)
z := store.NewMap[string, int](s)

The Format of the Store can be set and one can get the value of a variable.

s = s.Format(
    func(s store.Store) any {
        return map[string]any{
            "x": x.Get(s),
            "y": y.Slice(s),
            "z": z.Map(s),
        }
    },
)

The Value of the Store can be set. When maximizing or minimizing, variable assignments are chosen so that this value increases or decreases, respectively.

s = s.Value(
    func(s store.Store) int {
        sum := 0
        for i := 0; i < y.Len(s); i++ {
            sum += y.Get(s, i)
        }
        return x.Get(s) + sum
    },
)

Changes, like setting a new value on a variable, can be applied to the Store.

s = s.Apply(
    x.Set(10),
    y.Append(5, 6),
)

To broaden the search space, new Stores can be generated.

s = s.Generate(func(s store.Store) store.Generator {
    value := x.Get(s)
    return store.Lazy(
        func() bool {
            return value <= 10
        },
        func() store.Store {
            value++
            return s.Apply(x.Set(value))
        },
    )
})

To check the operational validity of the Store (all decisions have been made and they are valid), use the provided function.

s = s.Validate(func(s store.Store) bool {
    return x.Get(s)%2 == 0
})

When setting a Value, it can be maximized or minimized. Alternatively, operational validity on the Store can be satisfied, in which case setting a Value is not needed. Options are required to specify the search mechanics.

// DefaultOptions provide sensible defaults.
opt := store.DefaultOptions()
// Options can be modified, e.g.: changing the duration.
// opt.Limits.Duration = time.Duration(4) * time.Second
solver := s.Value(...).Minimizer(opt)
// solver := s.Value(...).Minimizer(opt)
// solver := s.Satisfier(opt)

To find the best collection of variable assignments in the Store, the last Solution can be obtained from the given Solver. Alternatively, all Solutions can be retrieved to debug the search mechanics of the Solver.

solver := s.Maximizer(opt)
last := solver.Last(context.Background())
// all := solver.All(context.Background())
best := x.Get(last.Store)
stats := last.Statistics

Runners are provided for convenience when running the Store. They read data and options and manage the call to the Solver. The `NEXTMV_RUNNER` environment variable defines the type of runner used.

  • "cli": (Default) Command Line Interface runner. Useful for running from a terminal. Can read from a file or stdin and write to a file or stdout.
  • "http": HTTP runner. Useful for sending requests and receiving responses on the specified port.

The runner receives a handler that specifies the data type and expects a Solver.

package main

import (
    "github.com/nextmv-io/sdk/run"
    "github.com/nextmv-io/sdk/store"
)

func main() {
    handler := func(v int, opt store.Options) (store.Solver, error) {
        s := store.New()
        x := store.NewVar(s, v)      // Initialized from the runner.
        s = s.Value(...).Format(...) // Modify the Store.
        return s.Maximizer(opt), nil // Options are passed by the runner.
    }
    run.Run(handler)
}

Compile the binary and use the -h flag to see available options to configure a runner. You can use command-line flags or environment variables. When using environment variables, use all caps and snake case. For example, using the command-line flag `-limits.duration` is equivalent to setting the environment variable `HOP_SOLVER_LIMITS_DURATION`.

Using the cli runner for example:

echo 0 | go run main.go -limits.duration 2s

Writes this output to stdout:

{
  "hop": {
    "version": "..."
  },
  "options": {
    "diagram": {
      "expansion": {
        "limit": 0
      },
      "width": 10
    },
    "limits": {
      "duration": "2s"
    },
    "search": {
      "buffer": 100
    },
    "sense": "maximizer"
  },
  "store": {
    "x": 10
  },
  "statistics": {
    "bounds": {
      "lower": 10,
      "upper": 9223372036854776000
    },
    "search": {
      "generated": 10,
      "filtered": 0,
      "expanded": 10,
      "reduced": 0,
      "restricted": 10,
      "deferred": 0,
      "explored": 1,
      "solutions": 5
    },
    "time": {
      "elapsed": "93.417µs",
      "elapsed_seconds": 9.3417e-05,
      "start": "..."
    },
    "value": 10
  }
}

Deprecated: This package is deprecated and will be removed in the next major release.

Example (KnightsTour)

A knight's tour is a sequence of moves of a knight on an nxn chessboard such that the knight visits every square exactly once. This example implements an open knight's tour, given that the last position will not necessarily be one move away from the first.

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"reflect"
	"sort"
	"strconv"
	"strings"

	"github.com/nextmv-io/sdk/store"
)

// position tracks the knight on the board.
type position struct {
	row int
	col int
}

// offsets for obtaining the eight different positions a knight can reach from
// any given square (inside the board or not).
var offsets = []struct {
	row int
	col int
}{
	{2, 1}, {1, 2}, {-1, 2}, {-2, 1}, {-2, -1}, {-1, -2}, {1, -2}, {2, -1},
}

// onward implements sort.Interface for []Position based on the moves field.
type onward struct {
	moves      []int
	candidates []position
}

func (o onward) Len() int { return len(o.moves) }
func (o onward) Swap(i, j int) {
	o.moves[i], o.moves[j] = o.moves[j], o.moves[i]
	o.candidates[i], o.candidates[j] = o.candidates[j], o.candidates[i]
}
func (o onward) Less(i, j int) bool { return o.moves[i] < o.moves[j] }

// positions returns the positions that a knight can move to from the given row
// and column, asserting that they are inside the board and have not been
// visited yet.
func positions(n, row, col int, tour []position) []position {
	// Create all the possible movement options.
	options := make([]position, len(offsets))
	for i := range options {
		options[i] = position{
			row: row + offsets[i].row,
			col: col + offsets[i].col,
		}
	}

	// Create positions that are inside the board and have not been visited
	// yet.
	var positions []position
	for _, opt := range options {
		// Assert the option is inside the board.
		if opt.row >= 0 && opt.row < n && opt.col >= 0 && opt.col < n {
			if !visited(opt, tour) {
				positions = append(positions, opt)
			}
		}
	}

	return positions
}

// visited asserts that the option has not been visited in the tour.
func visited(opt position, tour []position) bool {
	visited := false
	for _, move := range tour {
		if reflect.DeepEqual(opt, move) {
			visited = true
			break
		}
	}
	return visited
}

/*
format defines the JSON formatting of the store as a board, e.g.

	{
		"0": "00 -- 02 -- -- ",
		"1": "-- -- -- -- 03 ",
		"2": "06 01 -- -- -- ",
		"3": "-- -- 07 04 -- ",
		"4": "-- 05 -- -- -- "
	}
*/
func format(tour store.Slice[position], n int) func(s store.Store) any {
	return func(s store.Store) any {
		// Empty board.
		board := map[string]string{}
		for i := 0; i < n; i++ {
			board[strconv.Itoa(i)] = strings.Repeat("-- ", n)
		}

		// Loop over the knight's tour to fill the board with positions.
		for i, p := range tour.Slice(s) {
			// Make every number a double digit.
			num := strconv.Itoa(i)
			if i < 10 {
				num = "0" + num
			}

			// Set the visited position on the board.
			cols := strings.Split(board[strconv.Itoa(p.row)], " ")
			cols[p.col] = num
			board[strconv.Itoa(p.row)] = strings.Join(cols, " ")
		}

		return board
	}
}

// A knight's tour is a sequence of moves of a knight on an nxn chessboard such
// that the knight visits every square exactly once. This example implements an
// open knight's tour, given that the last position will not necessarily be one
// move away from the first.
func main() {
	// Board size and initial position.
	n := 5
	p := position{row: 0, col: 0}

	// Create the knight's tour model.
	knight := store.New()

	// Track the sequence of moves.
	tour := store.NewSlice(knight, p)

	// Define the output format.
	knight = knight.Format(format(tour, n))

	// The store is operationally valid if the tour is complete.
	knight = knight.Validate(func(s store.Store) bool {
		return tour.Len(s) == n*n
	})

	// Define the generation of the tour.
	knight = knight.Generate(func(s store.Store) store.Generator {
		// Gets the last move made and all the candidate positions from
		// there.
		lastMove := tour.Get(s, tour.Len(s)-1)
		candidates := positions(n, lastMove.row, lastMove.col, tour.Slice(s))

		// Obtain the number of onward moves per candidate, excluding
		// visited squares. Sort candidates increasingly by the number
		// of onward moves.
		moves := make([]int, len(candidates))
		for i, candidate := range candidates {
			moves[i] = len(positions(n, candidate.row, candidate.col, tour.Slice(s)))
		}
		onward := onward{moves: moves, candidates: candidates}
		sort.Sort(onward)

		// Starting from the most constrained candidate, create a store
		// queue by adding each candidate to the tour.
		stores := make([]store.Store, len(onward.candidates))
		for i, candidate := range onward.candidates {
			stores[i] = s.Apply(tour.Append(candidate))
		}

		return store.Eager(stores...)
	})

	// The solver type is a satisfier because only operationally valid tours
	// are needed, there is no value associated.
	opt := store.DefaultOptions()
	opt.Limits.Solutions = 1
	opt.Diagram.Expansion.Limit = 1
	solver := knight.Satisfier(opt)

	// Get the last solution of the problem and print it.
	last := solver.Last(context.Background())
	b, err := json.MarshalIndent(last.Store, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
}
Output:

{
  "0": "00 21 10 15 06 ",
  "1": "11 16 07 20 09 ",
  "2": "24 01 22 05 14 ",
  "3": "17 12 03 08 19 ",
  "4": "02 23 18 13 04 "
}
Example (LongestUncrossedKnightsPath)

The longest uncrossed (or nonintersecting) knight's path is a mathematical problem involving a knight on a square n×n chess board. The problem is to find the longest path the knight can take on the given board, such that the path does not intersect itself. Definitions reused from the knight's tour example: position, offsets, format, visited.

package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

// intersects returns true if the segment p0->p1 intersects with the segment
// p2->p3. It is based on this link:
// https://stackoverflow.com/a/14795484/15559724
func intersects(p0, p1, p2, p3 position) bool {
	s10x := p1.row - p0.row
	s10y := p1.col - p0.col
	s32x := p3.row - p2.row
	s32y := p3.col - p2.col

	denom := s10x*s32y - s32x*s10y
	if denom == 0 {
		return false
	}
	denomPositive := denom > 0

	s02x := p0.row - p2.row
	s02y := p0.col - p2.col
	sNumer := s10x*s02y - s10y*s02x
	if (sNumer < 0) == denomPositive {
		return false
	}

	tNumer := s32x*s02y - s32y*s02x
	if (tNumer < 0) == denomPositive {
		return false
	}

	if ((sNumer > denom) == denomPositive) || ((tNumer > denom) == denomPositive) {
		return false
	}

	return true
}

// intersection asserts that the option does not intersect the tour.
func intersection(opt position, tour []position, visited bool) bool {
	intersection := false
	if len(tour) >= 3 && !visited {
		for i := 0; i < len(tour)-1; i++ {
			if intersects(tour[i], tour[i+1], tour[len(tour)-1], opt) {
				intersection = true
				break
			}
		}
	}
	return intersection
}

// unintersected returns the positions that a knight can move to from the given
// row and column, asserting that they are inside the board, have not been
// visited yet and do not intersect the knight's path.
func unintersected(n, row, col int, tour []position) []position {
	// Create all the possible movement options.
	options := make([]position, len(offsets))
	for i := range options {
		options[i] = position{
			row: row + offsets[i].row,
			col: col + offsets[i].col,
		}
	}

	// Create positions that are feasible candidates.
	var positions []position
	for _, opt := range options {
		// Assert the option is inside the board, has not been visited and does
		// not intersect the path.
		if opt.row >= 0 && opt.row < n && opt.col >= 0 && opt.col < n {
			visited := visited(opt, tour)
			if !visited && !intersection(opt, tour, visited) {
				positions = append(positions, opt)
			}
		}
	}

	return positions
}

// The longest uncrossed (or nonintersecting) knight's path is a mathematical
// problem involving a knight on a square n×n chess board. The problem is to
// find the longest path the knight can take on the given board, such that the
// path does not intersect itself. Definitions reused from the knight's tour
// example: position, offsets, format, visited.
func main() {
	// Board size and initial position.
	n := 5
	p := position{row: 0, col: 0}

	// Create the knight's tour model.
	knight := store.New()

	// Track the sequence of moves.
	tour := store.NewSlice(knight, p)

	// Define the output format.
	knight = knight.Format(format(tour, n))

	// Define the value to maximize: the number of jumps made.
	knight = knight.Value(func(s store.Store) int { return tour.Len(s) - 1 })

	// Define the generation of the tour. The store is always operationally
	// valid.
	knight = knight.Generate(func(s store.Store) store.Generator {
		// Gets the last move made and all the candidate positions from
		// there.
		lastMove := tour.Get(s, tour.Len(s)-1)
		candidates := unintersected(
			n,
			lastMove.row,
			lastMove.col,
			tour.Slice(s),
		)

		// Create new stores by adding each candidate to the tour.
		stores := make([]store.Store, len(candidates))
		for i, candidate := range candidates {
			stores[i] = s.Apply(tour.Append(candidate))
		}

		return store.Eager(stores...)
	})

	// The solver type is a maximizer because the store is searching for the
	// highest number of moves.
	solver := knight.Maximizer(store.DefaultOptions())

	// Get the last solution of the problem and print it.
	last := solver.Last(context.Background())
	b, err := json.MarshalIndent(last.Store, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
}
Output:

{
  "0": "00 -- 02 -- -- ",
  "1": "-- -- -- -- 03 ",
  "2": "06 01 -- -- -- ",
  "3": "-- -- 07 04 -- ",
  "4": "-- 05 -- -- -- "
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func False deprecated

func False(s Store) bool

False is a convenience function that is always false.

Deprecated: This package is deprecated and will be removed in the next major release.

Example
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	f := store.False(s)
	fmt.Println(f)
}
Output:

false

func True deprecated

func True(s Store) bool

True is a convenience function that is always true.

Deprecated: This package is deprecated and will be removed in the next major release.

Example
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	f := store.True(s)
	fmt.Println(f)
}
Output:

true

Types

type Bounder deprecated

type Bounder func(Store) Bounds

Bounder maps a Store to monotonically tightening bounds. It is meant to be used with the store.Bound function.

Deprecated: This package is deprecated and will be removed in the next major release.

type Bounds deprecated

type Bounds struct {
	Lower int `json:"lower"`
	Upper int `json:"upper"`
}

Bounds on an objective value at some node in the search tree consist of a lower value and an upper value. If the lower and upper value are the same, the bounds have converged.

Deprecated: This package is deprecated and will be removed in the next major release.

type Change deprecated

type Change func(Store)

Change a Store.

Deprecated: This package is deprecated and will be removed in the next major release.

Example

Changes can be applied to a store.

package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	// Original value.
	s := store.New()
	x := store.NewVar(s, 15)
	fmt.Println(x.Get(s))

	// Value after store changed.
	s = s.Apply(x.Set(42))
	fmt.Println(x.Get(s))
}
Output:

15
42

type Condition deprecated

type Condition func(Store) bool

Condition represents a logical condition on a Store.

Deprecated: This package is deprecated and will be removed in the next major release.

Example

Custom conditions can be defined.

package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	a := func(store.Store) bool { return 1 < 2 }
	b := func(store.Store) bool { return 1 > 2 }
	c := store.And(a, b)(s)
	fmt.Println(c)
}
Output:

false

func And deprecated

func And(c1 Condition, c2 Condition, conditions ...Condition) Condition

And uses the conditional "AND" logical operator on all given conditions. It returns true if all conditions are true.

Deprecated: This package is deprecated and will be removed in the next major release.

Example
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	c := store.And(store.True, store.False)(s)
	fmt.Println(c)
	c = store.And(store.True, store.True)(s)
	fmt.Println(c)
	c = store.And(store.False, store.True)(s)
	fmt.Println(c)
	c = store.And(store.False, store.False)(s)
	fmt.Println(c)
	c = store.And(store.False, store.True, store.False)(s)
	fmt.Println(c)
	c = store.And(store.True, store.True, store.True)(s)
	fmt.Println(c)
}
Output:

false
true
false
false
false
true

func Not deprecated

func Not(c Condition) Condition

Not negates the given condition.

Deprecated: This package is deprecated and will be removed in the next major release.

Example
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	f := store.Not(store.True)(s)
	fmt.Println(f)
	t := store.Not(store.False)(s)
	fmt.Println(t)
}
Output:

false
true

func Or deprecated

func Or(c1 Condition, c2 Condition, conditions ...Condition) Condition

Or uses the conditional "OR" logical operator on all given conditions. It returns true if at least one condition is true.

Deprecated: This package is deprecated and will be removed in the next major release.

Example
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	c := store.Or(store.True, store.False)(s)
	fmt.Println(c)
	c = store.Or(store.True, store.True)(s)
	fmt.Println(c)
	c = store.Or(store.False, store.True)(s)
	fmt.Println(c)
	c = store.Or(store.False, store.False)(s)
	fmt.Println(c)
	c = store.Or(store.False, store.True, store.False)(s)
	fmt.Println(c)
	c = store.Or(store.True, store.True, store.True)(s)
	fmt.Println(c)
}
Output:

true
true
true
false
true
true

func Xor deprecated

func Xor(c1, c2 Condition) Condition

Xor uses the conditional "Exclusive OR" logical operator on all given conditions. It returns true if, and only if, the conditions are different.

Deprecated: This package is deprecated and will be removed in the next major release.

Example
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	c := store.Xor(store.True, store.False)(s)
	fmt.Println(c)
	c = store.Xor(store.True, store.True)(s)
	fmt.Println(c)
	c = store.Xor(store.False, store.True)(s)
	fmt.Println(c)
	c = store.Xor(store.False, store.False)(s)
	fmt.Println(c)
}
Output:

true
false
true
false

type Diagram deprecated

type Diagram struct {
	// Maximum Width of the Decision Diagram.
	Width int `usage:"diagram width" default:"10"`
	// Maximum Expansion that can be generated from a Store.
	Expansion struct {
		// Limit represents the maximum number of children Stores that can
		// be generated from a parent.
		Limit int `json:"limit" usage:"diagram expansion limit" default:"1"`
	}
}

Diagram options. The Store search is based on Decision Diagrams. These options configure the mechanics of using DD.

Deprecated: This package is deprecated and will be removed in the next major release.

func (Diagram) MarshalJSON deprecated

func (d Diagram) MarshalJSON() ([]byte, error)

MarshalJSON Diagram.

Deprecated: This package is deprecated and will be removed in the next major release.

type Domain deprecated

type Domain interface {
	/*
		Add values to a Domain.

			s1 := store.New()
			d := store.Multiple(s1, 1, 3, 5)
			s2 := s1.Apply(d.Add(2, 4))

			d.Domain(s1) // {1, 3, 5}}
			d.Domain(s2) // [1, 5]]

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Add(...int) Change

	/*
		AtLeast updates the Domain to the sub-Domain of at least some value.

			s1 := store.New()
			d := store.NewDomain(s1, model.NewRange(1, 10), model.NewRange(101, 110))
			s2 := s1.Apply(d.AtLeast(50))

			d.Domain(s1) // {[1, 10], [101, 110]}
			d.Domain(s2) // [101, 110]

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	AtLeast(int) Change

	/*
		AtMost updates the Domain to the sub-Domain of at most some value.

			s1 := store.New()
			d := store.NewDomain(s1, model.NewRange(1, 10), model.NewRange(101, 110))
			s2 := s1.Apply(d.AtMost(50))

			d.Domain(s1) // {[1, 10], [101, 110]}
			d.Domain(s2) // [1, 10]

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	AtMost(int) Change

	/*
		Cmp lexically compares two integer Domains. It returns a negative value
		if the receiver is less, 0 if they are equal, and a positive value if
		the receiver Domain is greater.

			s := store.New()
			d1 := store.NewDomain(s, model.NewRange(1, 5), model.NewRange(8, 10))
			d2 := store.Multiple(s, -1, 1)
			d1.Cmp(s, d2) // > 0

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Cmp(Store, Domain) int

	/*
		Contains returns true if a Domain contains a given value.

			s := store.New()
			d := store.NewDomain(s, model.NewRange(1, 10))
			d.Contains(s, 5)  // true
			d.Contains(s, 15) // false

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Contains(Store, int) bool

	/*
		Domain returns a Domain unattached to a Store.

			s := store.New()
			d := store.NewDomain(s, model.NewRange(1, 10))
			d.Domain(s) // model.NewDomain(model.NewRange(1, 10))

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Domain(Store) model.Domain

	/*
		Empty is true if a Domain is empty for a Store.

			s := store.New()
			d1 := store.NewDomain(s)
			d2 := store.Singleton(s, 42)
			d1.Empty(s) // true
			d2.Empty(s) // false

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Empty(Store) bool

	/*
		Len of a Domain, counting all values within ranges.

			s := store.New()
			d := store.NewDomain(s, model.NewRange(1, 10), model.NewRange(-5, -1))
			d.Len(s) // 15

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Len(Store) int

	/*
		Max of a Domain and a boolean indicating it is non-empty.

			s := store.New()
			d1 := store.NewDomain(s)
			d2 := store.NewDomain(s, model.NewRange(1, 10), model.NewRange(-5, -1))
			d1.Max(s) // returns (_, false)
			d2.Max(s) // returns (10, true)

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Max(Store) (int, bool)

	/*
		Min of a Domain and a boolean indicating it is non-empty.

			s := store.New()
			d1 := store.NewDomain(s)
			d2 := store.NewDomain(s, model.NewRange(1, 10), model.NewRange(-5, -1))
			d1.Min(s) // returns (_, false)
			d2.Min(s) // returns (-5, true)

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Min(Store) (int, bool)

	/*
		Remove values from a Domain.

			s1 := store.New()
			d := store.NewDomain(s1, model.NewRange(1, 5))
			s2 := s1.Apply(d.Remove([]int{2, 4}))

			d.Domain(s1) // [1, 5]
			d.Domain(s2) // {1, 3, 5}

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Remove([]int) Change

	/*
		Slice representation of a Domain.

			s := store.New()
			d := store.NewDomain(s, model.NewRange(1, 5))
			d.Slice(s) // [1, 2, 3, 4, 5]

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Slice(Store) []int

	/*
		Value returns an int and true if a Domain is Singleton.

			s := store.New()
			d1 := store.NewDomain(s)
			d2 := store.Singleton(s, 42)
			d3 := store.Multiple(s, 1, 3, 5)
			d1.Value(s) // returns (0, false)
			d2.Value(s) // returns (42, true)
			d3.Value(s) // returns (0, false)

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Value(Store) (int, bool)
}

A Domain of integers.

Deprecated: This package is deprecated and will be removed in the next major release.

Example (Add)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	d := store.Multiple(s1, 1, 3, 5)
	s2 := s1.Apply(d.Add(2, 4))
	fmt.Println(d.Domain(s1))
	fmt.Println(d.Domain(s2))
}
Output:

{[{1 1} {3 3} {5 5}]}
{[{1 5}]}
Example (AtLeast)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	d := store.NewDomain(s1, model.NewRange(1, 10), model.NewRange(101, 110))
	s2 := s1.Apply(d.AtLeast(50))
	fmt.Println(d.Domain(s1))
	fmt.Println(d.Domain(s2))
}
Output:

{[{1 10} {101 110}]}
{[{101 110}]}
Example (AtMost)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	d := store.NewDomain(s1, model.NewRange(1, 10), model.NewRange(101, 110))
	s2 := s1.Apply(d.AtMost(50))
	fmt.Println(d.Domain(s1))
	fmt.Println(d.Domain(s2))
}
Output:

{[{1 10} {101 110}]}
{[{1 10}]}
Example (Cmp)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d1 := store.NewDomain(s, model.NewRange(1, 5), model.NewRange(8, 10))
	d2 := store.Multiple(s, -1, 1)
	fmt.Println(d1.Cmp(s, d2))
}
Output:

1
Example (Contains)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.NewDomain(s, model.NewRange(1, 10))
	fmt.Println(d.Contains(s, 5))
	fmt.Println(d.Contains(s, 15))
}
Output:

true
false
Example (Domain)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.NewDomain(s, model.NewRange(1, 10))
	fmt.Println(d.Domain(s))
}
Output:

{[{1 10}]}
Example (Empty)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d1 := store.NewDomain(s)
	d2 := store.Singleton(s, 42)
	fmt.Println(d1.Empty(s))
	fmt.Println(d2.Empty(s))
}
Output:

true
false
Example (Len)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.NewDomain(s, model.NewRange(1, 10), model.NewRange(-5, -1))
	fmt.Println(d.Len(s))
}
Output:

15
Example (Max)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d1 := store.NewDomain(s)
	d2 := store.NewDomain(s, model.NewRange(1, 10), model.NewRange(-5, -1))
	fmt.Println(d1.Max(s))
	fmt.Println(d2.Max(s))
}
Output:

9223372036854775807 false
10 true
Example (Min)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d1 := store.NewDomain(s)
	d2 := store.NewDomain(s, model.NewRange(1, 10), model.NewRange(-5, -1))
	fmt.Println(d1.Min(s))
	fmt.Println(d2.Min(s))
}
Output:

-9223372036854775808 false
-5 true
Example (Remove)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	d := store.NewDomain(s1, model.NewRange(1, 5))
	s2 := s1.Apply(d.Remove([]int{2, 4}))
	fmt.Println(d.Domain(s1))
	fmt.Println(d.Domain(s2))
}
Output:

{[{1 5}]}
{[{1 1} {3 3} {5 5}]}
Example (Slice)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.NewDomain(s, model.NewRange(1, 5))
	fmt.Println(d.Slice(s))
}
Output:

[1 2 3 4 5]
Example (Value)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d1 := store.NewDomain(s)
	d2 := store.Singleton(s, 42)
	d3 := store.Multiple(s, 1, 3, 5)
	fmt.Println(d1.Value(s))
	fmt.Println(d2.Value(s))
	fmt.Println(d3.Value(s))
}
Output:

0 false
42 true
0 false

func Multiple deprecated

func Multiple(s Store, values ...int) Domain

Multiple creates a Domain containing multiple integer values and stores it in a Store.

s := store.New()
even := store.Multiple(s, 2, 4, 6, 8)

Deprecated: This package is deprecated and will be removed in the next major release.

Example
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	even := store.Multiple(s, 2, 4, 6, 8)
	fmt.Println(even.Domain(s))
}
Output:

{[{2 2} {4 4} {6 6} {8 8}]}

func NewDomain deprecated

func NewDomain(s Store, ranges ...model.Range) Domain

NewDomain creates a Domain of integers and stores it in a Store.

s := store.New()
d1 := store.NewDomain(s, model.NewRange(1, 10)) // 1 through 10
d2 := store.NewDomain( // 1 through 10 and 20 through 29
	s,
	model.NewRange(1, 10),
	model.NewRange(20, 29),
)

Deprecated: This package is deprecated and will be removed in the next major release.

Example
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d1 := store.NewDomain(s, model.NewRange(1, 10))
	d2 := store.NewDomain(s, model.NewRange(1, 10), model.NewRange(20, 29))
	fmt.Println(d1.Domain(s))
	fmt.Println(d2.Domain(s))
}
Output:

{[{1 10}]}
{[{1 10} {20 29}]}

func Singleton deprecated

func Singleton(s Store, value int) Domain

Singleton creates a Domain containing one integer value and stores it in a Store.

s := store.New()
fortyTwo := store.Singleton(s, 42)

Deprecated: This package is deprecated and will be removed in the next major release.

Example
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	fortyTwo := store.Singleton(s, 42)
	fmt.Println(fortyTwo.Domain(s))
}
Output:

{[{42 42}]}

type Domains deprecated

type Domains interface {
	/*
		Add values to a Domain by index.

			s1 := store.New()
			d := store.Repeat(s1, 3, model.Singleton(42)) // [42, 42, 42]
			s2 := s1.Apply(d.Add(1, 41, 43))
			d.Domains(s2)                                 // [42, [41,43], 42]

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Add(int, ...int) Change

	/*
		Assign a Singleton value to a Domain by index.

			s1 := store.New()
			d := store.Repeat(s1, 3, model.Singleton(42)) // [42, 42, 42]
			s2 := s1.Apply(d.Assign(0, 10))
			d.Domains(s2)                                 // [10, 42, 42]

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Assign(int, int) Change

	/*
		AtLeast updates the Domain to the sub-Domain of at least some value.

			s1 := store.New()
			d := store.Repeat( // [[1, 100], [1, 100]]
				s1,
				2,
				model.NewDomain(model.NewRange(1, 100)),
			)
			s2 := s1.Apply(d.AtLeast(1, 50))
			d.Domains(s2) // [[1, 100], [50, 100]]

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	AtLeast(int, int) Change

	/*
		AtMost updates the Domain to the sub-Domain of at most some value.

			s1 := store.New()
			d := store.Repeat( // [[1, 100], [1, 100]]
				s1,
				2,
				model.NewDomain(model.NewRange(1, 100)),
			)
			s2 := s1.Apply(d.AtMost(1, 50))
			d.Domains(s2) // [[1, 100], [1, 50]]

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	AtMost(int, int) Change

	/*
		Cmp lexically compares two sequences of integer Domains. It returns a
		negative value if the receiver is less, 0 if they are equal, and a
		positive value if the receiver Domain is greater.

			s := store.New()
			d1 := store.Repeat(s, 2, model.Singleton(42)) // [42, 42, 42]
			d2 := store.Repeat(s, 3, model.Singleton(43)) // [43, 43]]
			d1.Cmp(s, d2) // < 0

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Cmp(Store, Domains) int

	/*
		Domain by index.

			s := store.New()
			d := store.NewDomains(s, model.NewDomain(), model.Singleton(42))
			d.Domain(s, 0) // {}
			d.Domain(s, 1) // 42

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Domain(Store, int) model.Domain

	/*
		Domains in the sequence.

			s := store.New()
			d := store.NewDomains(s, model.NewDomain(), model.Singleton(42))
			d.Domains(s) // [{}, 42}

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Domains(Store) model.Domains

	/*
		Empty is true if all Domains are empty.

			s := store.New()
			d := store.NewDomains(s, model.NewDomain())
			d.Empty(s) // true

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Empty(Store) bool

	/*
		Len returns the number of Domains.

			s := store.New()
			d := store.Repeat(s, 5, model.NewDomain())
			d.Len(s) // 5

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Len(Store) int

	/*
		Remove values from a Domain by index.

			s1 := store.New()
			d := store.NewDomains(s1, model.Multiple(42, 13)) // {13, 42}
			s2 := s1.Apply(d.Remove(0, []int{13}))
			d.Domains(s2) // {42}

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Remove(int, []int) Change

	/*
		Singleton is true if all Domains are Singleton.

			s := store.New()
			d := store.Repeat(s, 5, model.Singleton(42))
			d.Singleton(s) // true

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Singleton(Store) bool

	/*
		Slices converts Domains to a slice of int slices.

			s := store.New()
			d := store.NewDomains(s, model.NewDomain(), model.Multiple(1, 3))
			d.Slices(s) // [[], [1, 2, 3]]

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Slices(Store) [][]int

	/*
		Values returns the values of a sequence of Singleton Domains.

			s1 := store.New()
			d := store.Repeat(s1, 3, model.Singleton(42))
			s2 := s1.Apply(d.Add(0, 41))
			d.Values(s1) // ([42, 42, 42], true)
			d.Values(s2) // ([], false)

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Values(Store) ([]int, bool)

	/*
		First returns the first Domain index with length above 1 and true if it
		is found. If no Domain has a length above 1, the function returns 0 and
		false.

			s := store.New()
			d := store.NewDomains(
				s,
				model.Singleton(88),   // Length 1
				model.Multiple(1, 3),  // Length above 1
				model.Multiple(4, 76), // Length above 1
			)
			d.First(s) // (1, true)

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	First(Store) (int, bool)

	/*
		Largest returns the index of the largest Domain with length above 1 by
		number of elements and true if it is found. If no Domain has a length
		above 1, the function returns 0 and false.

		    s := store.New()
		    d := store.NewDomains(
		        s,
		        model.Singleton(88),       // Length 1
		        model.Multiple(1, 3),      // Length 2
		        model.Multiple(4, 76, 97), // Length 3
		    )
		    d.Largest(s) // (2, true)

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Largest(Store) (int, bool)

	/*
		Last returns the last Domain index with length above 1 and true if it
		is found. If no Domain has a length above 1, the function returns 0 and
		false.

		    s := store.New()
		    d := store.NewDomains(
		        s,
		        model.Singleton(88),       // Length 1
		        model.Multiple(1, 3),      // Length above 1
		        model.Multiple(4, 76, 97), // Length above 1
				model.Singleton(45),       // Length 1
		    )
		    d.Last(s) // (2, true)

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Last(Store) (int, bool)

	/*
		Maximum returns the index of the Domain containing the maximum value
		with length above 1 and true if it is found. If no Domain has a length
		above 1, the function returns 0 and false.

			s := store.New()
			d := store.NewDomains(
		        s,
		        model.Singleton(88),       // Length 1
		        model.Multiple(4, 76, 97), // Length above 1
		        model.Multiple(1, 3),      // Length above 1
				model.Singleton(45),       // Length 1
		    )
			d.Maximum(s) // (1, true)

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Maximum(Store) (int, bool)

	/*
		Minimum returns the index of the Domain containing the minimum value
		with length above 1 and true if it is found. If no Domain has a length
		above 1, the function returns 0 and false.

			s := store.New()
			d := store.NewDomains(
		        s,
		        model.Singleton(88),       // Length 1
		        model.Multiple(4, 76, 97), // Length above 1
		        model.Multiple(1, 3),      // Length above 1
				model.Singleton(45),       // Length 1
		    )
			d.Minimum(s) // (2, true)

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Minimum(Store) (int, bool)

	/*
		Smallest returns the index of the smallest Domain with length above 1
		by number of elements and true if it is found. If no Domain has a
		length above 1, the function returns 0 and false.

		    s := store.New()
		    d := store.NewDomains(
		        s,
		        model.Singleton(88),       // Length 1
		        model.Multiple(1, 3),      // Length 2
		        model.Multiple(4, 76, 97), // Length 3
		    )
		    d.Smallest(s) // (1, true)

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Smallest(Store) (int, bool)
}

Domains of integers.

Deprecated: This package is deprecated and will be removed in the next major release.

Example (Add)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	d := store.Repeat(s1, 3, model.Singleton(42))
	s2 := s1.Apply(d.Add(1, 41, 43))
	fmt.Println(d.Domains(s2))
}
Output:

[{[{42 42}]} {[{41 43}]} {[{42 42}]}]
Example (Assign)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	d := store.Repeat(s1, 3, model.Singleton(42))
	s2 := s1.Apply(d.Assign(0, 10))
	fmt.Println(d.Domains(s2))
}
Output:

[{[{10 10}]} {[{42 42}]} {[{42 42}]}]
Example (AtLeast)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	d := store.Repeat(s1, 2, model.NewDomain(model.NewRange(1, 100)))
	s2 := s1.Apply(d.AtLeast(1, 50))
	fmt.Println(d.Domains(s2))
}
Output:

[{[{1 100}]} {[{50 100}]}]
Example (AtMost)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	d := store.Repeat(s1, 2, model.NewDomain(model.NewRange(1, 100)))
	s2 := s1.Apply(d.AtMost(1, 50))
	fmt.Println(d.Domains(s2))
}
Output:

[{[{1 100}]} {[{1 50}]}]
Example (Cmp)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d1 := store.Repeat(s, 2, model.Singleton(42))
	d2 := store.Repeat(s, 3, model.Singleton(43))
	fmt.Println(d1.Cmp(s, d2))
}
Output:

-1
Example (Domain)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.NewDomains(s, model.NewDomain(), model.Singleton(42))
	fmt.Println(d.Domain(s, 0))
	fmt.Println(d.Domain(s, 1))
}
Output:

{[]}
{[{42 42}]}
Example (Domains)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.NewDomains(s, model.NewDomain(), model.Singleton(42))
	fmt.Println(d.Domains(s))
}
Output:

[{[]} {[{42 42}]}]
Example (Empty)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.NewDomains(s, model.NewDomain())
	fmt.Println(d.Empty(s))
}
Output:

true
Example (First)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.NewDomains(
		s,
		model.Singleton(88),
		model.Multiple(1, 3),
		model.Multiple(4, 76),
	)
	fmt.Println(d.First(s))
}
Output:

1 true
Example (Largest)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.NewDomains(
		s,
		model.Singleton(88),
		model.Multiple(1, 3),
		model.Multiple(4, 76, 97),
	)
	fmt.Println(d.Largest(s))
}
Output:

2 true
Example (Last)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.NewDomains(
		s,
		model.Singleton(88),
		model.Multiple(1, 3),
		model.Multiple(4, 76, 97),
		model.Singleton(45),
	)
	fmt.Println(d.Last(s))
}
Output:

2 true
Example (Len)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.Repeat(s, 5, model.NewDomain())
	fmt.Println(d.Len(s))
}
Output:

5
Example (Maximum)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.NewDomains(
		s,
		model.Singleton(88),
		model.Multiple(4, 76, 97),
		model.Multiple(1, 3),
		model.Singleton(45),
	)
	fmt.Println(d.Maximum(s))
}
Output:

1 true
Example (Minimum)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.NewDomains(
		s,
		model.Singleton(88),
		model.Multiple(4, 76, 97),
		model.Multiple(1, 3),
		model.Singleton(45),
	)
	fmt.Println(d.Minimum(s))
}
Output:

2 true
Example (Remove)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	d := store.NewDomains(s1, model.Multiple(42, 13))
	s2 := s1.Apply(d.Remove(0, []int{13}))
	fmt.Println(d.Domains(s2))
}
Output:

[{[{42 42}]}]
Example (Singleton)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.Repeat(s, 5, model.Singleton(42))
	fmt.Println(d.Singleton(s))
}
Output:

true
Example (Slices)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.NewDomains(s, model.NewDomain(), model.Multiple(1, 3))
	fmt.Println(d.Slices(s))
}
Output:

[[] [1 3]]
Example (Smallest)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.NewDomains(
		s,
		model.Singleton(88),
		model.Multiple(1, 3),
		model.Multiple(4, 76, 97),
	)
	fmt.Println(d.Smallest(s))
}
Output:

1 true
Example (Values)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	d := store.Repeat(s1, 3, model.Singleton(42))
	s2 := s1.Apply(d.Add(0, 41))
	fmt.Println(d.Values(s1))
	fmt.Println(d.Values(s2))
}
Output:

[42 42 42] true
[] false

func NewDomains deprecated

func NewDomains(s Store, domains ...model.Domain) Domains

NewDomains creates a sequence of Domains and stores the sequence in a Store.

s := store.New()
d := store.NewDomains( // [1 to 10, 42, odds]
	s,
	model.NewDomain(model.NewRange(1, 10)),
	model.Singleton(42),
	model.Multiple(1, 3, 5, 7),
)

Deprecated: This package is deprecated and will be removed in the next major release.

Example
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.NewDomains( // [1 to 10, 42, odds]
		s,
		model.NewDomain(model.NewRange(1, 10)),
		model.Singleton(42),
		model.Multiple(1, 3, 5, 7),
	)
	fmt.Println(d.Domains(s))
}
Output:

[{[{1 10}]} {[{42 42}]} {[{1 1} {3 3} {5 5} {7 7}]}]

func Repeat deprecated

func Repeat(s Store, n int, domain model.Domain) Domains

Repeat a Domain n times and store the sequence in a Store.

s := store.New()
d := store.Repeat(s, 3, model.NewDomain(model.NewRange(1, 10)))

Deprecated: This package is deprecated and will be removed in the next major release.

Example
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/model"
	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	d := store.Repeat(s, 3, model.NewDomain(model.NewRange(1, 10)))
	fmt.Println(d.Domains(s))
}
Output:

[{[{1 10}]} {[{1 10}]} {[{1 10}]}]

type Formatter deprecated

type Formatter func(Store) any

Formatter maps a Store to any type with a JSON representation. It is meant to be used with the store.Format function.

Deprecated: This package is deprecated and will be removed in the next major release.

type Generator deprecated

type Generator any

A Generator is used to generate new Stores (children) from an existing one (parent). It is meant to be used with the store.Generate function.

Deprecated: This package is deprecated and will be removed in the next major release.

func Eager deprecated

func Eager(s ...Store) Generator

Eager way of generating new Stores. The Generator uses the list of Stores upfront in the order they are provided.

Deprecated: This package is deprecated and will be removed in the next major release.

Example

Generate stores eagerly: create all stores from an integer variable by increasing its value in 1 each time. The value should never be greater than 5. Eager implementation of the Lazy example.

package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewVar(s, 0)
	solver := s.
		Generate(func(s store.Store) store.Generator {
			value := x.Get(s)
			var stores []store.Store
			for value <= 5 {
				value++
				if value > 5 {
					break
				}
				stores = append(stores, s.Apply(x.Set(value)))
			}
			return store.Eager(stores...)
		}).
		Value(func(s store.Store) int { return x.Get(s) }).
		Format(func(s store.Store) any { return x.Get(s) }).
		Maximizer(store.DefaultOptions())

	// Get the last solution of the problem and print it.
	last := solver.Last(context.Background())
	b, err := json.MarshalIndent(last.Store, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
}
Output:

5

func Lazy deprecated

func Lazy(c func() bool, f func() Store) Generator

Lazy way of generating new Stores. While the condition holds, the function is called to generate new Stores. If the condition is no longer true or a nil Store is returned, the generator is not used anymore by the current parent.

Deprecated: This package is deprecated and will be removed in the next major release.

Example

Generate stores lazily: while an integer variable is less than or equal to 5, increase its value by 1. Lazy implementation of the Eager example.

package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewVar(s, 0)
	solver := s.
		Generate(func(s store.Store) store.Generator {
			value := x.Get(s)
			return store.Lazy(
				func() bool {
					return value <= 5
				},
				func() store.Store {
					value++
					return s.Apply(x.Set(value))
				},
			)
		}).
		Value(func(s store.Store) int { return x.Get(s) }).
		Format(func(s store.Store) any { return x.Get(s) }).
		Maximizer(store.DefaultOptions())

	// Get the last solution of the problem and print it.
	last := solver.Last(context.Background())
	b, err := json.MarshalIndent(last.Store, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
}
Output:

6

type Key deprecated

type Key interface{ int | string }

A Key for a Map.

Deprecated: This package is deprecated and will be removed in the next major release.

type Limits

type Limits struct {
	// Time Duration.
	Duration time.Duration `usage:"time limit, e.g. 10ms"`
	// Nodes represent active Stores in the search.
	Nodes int `usage:"node limit"`
	// Solutions represent operationally valid Stores.
	Solutions int `usage:"solution limit"`
}

Limits when performing a search. The search will stop if any one of these limits are encountered.

func (Limits) MarshalJSON deprecated

func (l Limits) MarshalJSON() ([]byte, error)

MarshalJSON Limits.

Deprecated: This package is deprecated and will be removed in the next major release.

type Map deprecated

type Map[K Key, V any] interface {
	/*
		Delete a Key from the Map.

			s1 := store.New()
			m := store.NewMap[int, string](s1)
			s1 = s1.Apply( // {42: foo, 13: bar}
				m.Set(42, "foo"),
				m.Set(13, "bar"),
			)
			s2 := s1.Apply(m.Delete(42)) // {13: bar}

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Delete(K) Change

	/*
		Get a value for a Key. If the Key is not present in the Map for the
		given Store, the zero value and false are returned.

			s1 := store.New()
			m := store.NewMap[int, string](s1)
			s2 := s1.Apply(m.Set(42, "foo"))
			m.Get(s2, 42) // (foo, true)
			m.Get(s2, 88) // (_, false)

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Get(Store, K) (V, bool)

	/*
		Len returns the number of Keys in a Map.

			s1 := store.New()
			m := store.NewMap[int, string](s1)
			s2 := s1.Apply(
				m.Set(42, "foo"),
				m.Set(13, "bar"),
			)
			m.Len(s1) // 0
			m.Len(s2) // 2

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Len(Store) int

	/*
		Map representation that is mutable.

			s1 := store.New()
			m := store.NewMap[int, string](s1)
			s2 := s1.Apply(
				m.Set(42, "foo"),
				m.Set(13, "bar"),
			)
			m.Map(s2) // map[int]string{42: "foo", 13: "bar"}

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Map(Store) map[K]V

	/*
		Set a Key to a Value.

			s1 := store.New()
			m := store.NewMap[int, string](s1)
			s2 := s1.Apply(m.Set(42, "foo")) // 42 -> foo
			s3 := s2.Apply(m.Set(42, "bar")) // 42 -> bar

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Set(K, V) Change
}

A Map stores key-value pairs in a Store.

Deprecated: This package is deprecated and will be removed in the next major release.

Example (Delete)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	m := store.NewMap[int, string](s1)
	s1 = s1.Apply(
		m.Set(42, "foo"),
		m.Set(13, "bar"),
	)
	s2 := s1.Apply(m.Delete(42))
	fmt.Println(m.Map(s1))
	fmt.Println(m.Map(s2))
}
Output:

map[13:bar 42:foo]
map[13:bar]
Example (Get)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	m := store.NewMap[int, string](s1)
	s2 := s1.Apply(m.Set(42, "foo"))
	fmt.Println(m.Get(s2, 42))
	fmt.Println(m.Get(s2, 88))
}
Output:

foo true
 false
Example (Len)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	m := store.NewMap[int, string](s1)
	s2 := s1.Apply(
		m.Set(42, "foo"),
		m.Set(13, "bar"),
	)
	fmt.Println(m.Len(s1))
	fmt.Println(m.Len(s2))
}
Output:

0
2
Example (Map)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	m := store.NewMap[int, string](s1)
	s2 := s1.Apply(
		m.Set(42, "foo"),
		m.Set(13, "bar"),
	)
	fmt.Println(m.Map(s2))
}
Output:

map[13:bar 42:foo]
Example (Set)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	m := store.NewMap[int, string](s1)
	s2 := s1.Apply(m.Set(42, "foo"))
	s3 := s2.Apply(m.Set(42, "bar"))
	fmt.Println(m.Map(s2))
	fmt.Println(m.Map(s3))
}
Output:

map[42:foo]
map[42:bar]

func NewMap deprecated

func NewMap[K Key, V any](s Store) Map[K, V]

NewMap returns a new NewMap and stores it in a Store.

s := store.New()
m1 := store.NewMap[int, [2]float64](s) // map of {int -> [2]float64}
m2 := store.NewMap[string, int](s)     // map of {string -> int}

Deprecated: This package is deprecated and will be removed in the next major release.

Example
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	m1 := store.NewMap[int, [2]float64](s)
	m2 := store.NewMap[string, int](s)
	s1 := s.Apply(m1.Set(2, [2]float64{0.1, 3.1416}))
	s2 := s.Apply(m2.Set("a", 43))
	fmt.Println(m1.Map(s1))
	fmt.Println(m2.Map(s2))
}
Output:

map[2:[0.1 3.1416]]
map[a:43]

type Options deprecated

type Options struct {
	Sense Sense
	// Tags are custom key-value pairs that the user defines for
	// record-keeping.
	Tags    map[string]any
	Diagram Diagram
	// Search options.
	Search struct {
		// Buffer represents the maximum number of Stores that can be buffered
		// when generating more Stores.
		Buffer int `usage:"solution buffer (0 = none)" default:"100"`
	}
	Limits Limits
	// Options for random number generation.
	Random struct {
		// Seed for generating random numbers.
		Seed int64 `json:"seed,omitempty" usage:"random seed"`
	}
	// Pool that is used in specific engines.
	Pool struct {
		// Maximum Size of the Pool.
		Size int `json:"size,omitempty" usage:"pool size (only specific engines)"`
	}
}

Options for a solver.

Deprecated: This package is deprecated and will be removed in the next major release.

func DefaultOptions deprecated

func DefaultOptions() Options

DefaultOptions for running a solver. Options can be customized after using these sensitive defaults.

opt := store.DefaultOptions()
opt.Limits.Duration = time.Duration(5) * time.Second

Deprecated: This package is deprecated and will be removed in the next major release.

Example

DefaultOptions provide sensible defaults but they can (and should) be modified.

package main

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	opt := store.DefaultOptions()
	b, err := json.MarshalIndent(opt, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))

	// Modify options
	opt.Diagram.Expansion.Limit = 1
	opt.Limits.Duration = time.Duration(4) * time.Second
	opt.Tags = map[string]any{"foo": 1, "bar": 2}
	b, err = json.MarshalIndent(opt, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
}
Output:

{
  "diagram": {
    "expansion": {
      "limit": 1
    },
    "width": 10
  },
  "search": {
    "buffer": 100
  },
  "sense": "minimize"
}
{
  "diagram": {
    "expansion": {
      "limit": 1
    },
    "width": 10
  },
  "limits": {
    "duration": "4s",
    "nodes": 0,
    "solutions": 0
  },
  "search": {
    "buffer": 100
  },
  "sense": "minimize",
  "tags": {
    "bar": 2,
    "foo": 1
  }
}

func (Options) MarshalJSON deprecated

func (o Options) MarshalJSON() ([]byte, error)

MarshalJSON Options.

Deprecated: This package is deprecated and will be removed in the next major release.

type Propagator deprecated

type Propagator func(Store) []Change

Propagator propagates Changes to a Store. It is meant to be used with the store.Propagate function.

Deprecated: This package is deprecated and will be removed in the next major release.

type Search struct {
	// Generated stores in the search.
	Generated int `json:"generated"`
	// Filtered stores in the search.
	Filtered int `json:"filtered"`
	// Expanded stores in the search.
	Expanded int `json:"expanded"`
	// Reduced stores in the search.
	Reduced int `json:"reduced"`
	// Restricted stores in the search.
	Restricted int `json:"restricted"`
	// Deferred stores in the search.
	Deferred int `json:"deferred"`
	// Explored stores in the search.
	Explored int `json:"explored"`
	// Operationally valid stores in the search.
	Solutions int `json:"solutions"`
}

Search statistics of the Store generation.

Deprecated: This package is deprecated and will be removed in the next major release.

type Sense deprecated

type Sense int

Sense specifies whether one is maximizing, minimizing, or satisfying. Default is set to minimization.

Deprecated: This package is deprecated and will be removed in the next major release.

const (
	// Minimize indicates the solution space is being searched to find the
	// smallest possible value.
	//
	// Deprecated: This package is deprecated and will be removed in the next major release.
	Minimize Sense = iota
	// Maximize indicates the solution space is being searched to find the
	// biggest possible value.
	//
	// Deprecated: This package is deprecated and will be removed in the next major release.
	Maximize
	// Satisfy indicates the solution space is being searched to find
	// operationally valid Stores.
	//
	// Deprecated: This package is deprecated and will be removed in the next major release.
	Satisfy
)

func (Sense) String

func (s Sense) String() string

type Slice deprecated

type Slice[T any] interface {
	/*
		Append one or more values to the end of a Slice.

			s1 := store.New()
			x := store.NewSlice(s1, 1, 2, 3) // [1, 2, 3]
			s2 := s1.Apply(x.Append(4, 5))
			x.Slice(s2) // [1, 2, 3, 4, 5]

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Append(value T, values ...T) Change

	/*
		Get an index of a Slice.

			s := store.New()
			x := store.NewSlice(s, 1, 2, 3)
			x.Get(s, 2) // 3

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Get(Store, int) T

	/*
		Insert one or more values at an index in a Slice.

			s1 := store.New()
			x := store.NewSlice(s1, "a", "b", "c")
			s2 := s1.Apply(x.Insert(2, "d", "e"))
			x.Slice(s2) // [a, b, d, e, c]

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Insert(index int, value T, values ...T) Change

	/*
		Len returns the length of a Slice.

			s := store.New()
			x := store.NewSlice(s, 1, 2, 3)
			x.Len(s) // 3

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Len(Store) int

	/*
		Prepend one or more values at the beginning of a Slice.

			s1 := store.New()
			x := store.NewSlice(s1, 1, 2, 3) // [1, 2, 3]
			s2 := s1.Apply(x.Prepend(4, 5))
			x.Slice(s2) // [4, 5, 1, 2, 3]

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Prepend(value T, values ...T) Change

	/*
		Remove a sub-Slice from a starting to an ending index.

			s1 := store.New()
			x := store.NewSlice(s1, 1, 2, 3) // [1, 2, 3]
			s2 := s1.Apply(x.Remove(1, 1))
			x.Slice(s2) // [1, 3]

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Remove(start, end int) Change

	/*
		Set a value by index.
			s1 := store.New()
			x := store.NewSlice(s1, "a", "b", "c") // [a, b, c]
			s2 := s1.Apply(x.Set(1, "d"))
			x.Slice(s2) // [a, d, c]

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Set(int, T) Change

	/*
		Slice representation that is mutable.

			s := store.New()
			x := store.NewSlice(s, 1, 2, 3)
			x.Slice(s) // []int{1, 2, 3}

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Slice(Store) []T
}

Slice manages an immutable slice container of some type in a Store.

Deprecated: This package is deprecated and will be removed in the next major release.

Example (Append)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	x := store.NewSlice(s1, 1, 2, 3)
	s2 := s1.Apply(x.Append(4, 5))
	fmt.Println(x.Slice(s2))
}
Output:

[1 2 3 4 5]
Example (Get)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewSlice(s, 1, 2, 3)
	fmt.Println(x.Get(s, 2))
}
Output:

3
Example (Insert)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	x := store.NewSlice(s1, "a", "b", "c")
	s2 := s1.Apply(x.Insert(2, "d", "e"))
	fmt.Println(x.Slice(s2))
}
Output:

[a b d e c]
Example (Len)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewSlice(s, 1, 2, 3)
	fmt.Println(x.Len(s))
}
Output:

3
Example (Prepend)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	x := store.NewSlice(s1, 1, 2, 3)
	s2 := s1.Apply(x.Prepend(4, 5))
	fmt.Println(x.Slice(s2))
}
Output:

[4 5 1 2 3]
Example (Remove)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	x := store.NewSlice(s1, 1, 2, 3)
	s2 := s1.Apply(x.Remove(1, 1))
	fmt.Println(x.Slice(s2))
}
Output:

[1 3]
Example (Set)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s1 := store.New()
	x := store.NewSlice(s1, "a", "b", "c")
	s2 := s1.Apply(x.Set(1, "d"))
	fmt.Println(x.Slice(s2))
}
Output:

[a d c]
Example (Slice)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewSlice(s, 1, 2, 3)
	fmt.Println(x.Slice(s))
}
Output:

[1 2 3]

func NewSlice deprecated

func NewSlice[T any](s Store, values ...T) Slice[T]

NewSlice returns a new NewSlice and stores it in a Store.

s := store.New()
x := store.NewSlice[int](s)        // []int{}
y := store.NewSlice(s, 3.14, 2.72) // []float64{3.14, 2.72}

Deprecated: This package is deprecated and will be removed in the next major release.

Example
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewSlice[int](s)
	y := store.NewSlice(s, 3.14, 2.72)
	fmt.Println(x.Slice(s))
	fmt.Println(y.Slice(s))
}
Output:

[]
[3.14 2.72]

type Solution deprecated

type Solution struct {
	// Store of the Solution. If nil, it means that the solution is
	// operationally invalid.
	Store      Store      `json:"store"`
	Statistics Statistics `json:"statistics"`
}

Solution of a decision automation problem. A Solution is an operationally valid Store.

Deprecated: This package is deprecated and will be removed in the next major release.

type Solver deprecated

type Solver interface {
	// All Solutions found by the Solver. Loop over the channel values to get
	// the solutions.
	All(context.Context) <-chan Solution

	// Last Solution found by the Solver. When running a Maximizer or
	// Minimizer, the last Solution is the best one found (highest or smallest
	// value, respectively) with the given options. Using this function is
	// equivalent to getting the last element when using All.
	Last(context.Context) Solution

	// Options provided to the Solver.
	Options() Options
}

A Solver searches a space and finds the best Solution possible, this is, the best collection of variable assignments in an operationally valid Store.

Deprecated: This package is deprecated and will be removed in the next major release.

Example (All)

Get all the solutions from the Generate example.

package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewVar(s, 0)
	s = s.Generate(func(s store.Store) store.Generator {
		value := x.Get(s)
		return store.Lazy(
			func() bool {
				return value <= 2
			},
			func() store.Store {
				value++
				return s.Apply(x.Set(value))
			},
		)
	})

	solver := s.
		Value(func(s store.Store) int { return x.Get(s) }).
		Format(func(s store.Store) any { return x.Get(s) }).
		Maximizer(store.DefaultOptions())

	// Get all solutions.
	all := solver.All(context.Background())

	// Loop over the channel values to get the solutions.
	solutions := make([]store.Store, len(all))
	for solution := range all {
		solutions = append(solutions, solution.Store)
	}

	// Print the solutions.
	b, err := json.MarshalIndent(solutions, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
}
Output:

[
  0,
  1,
  2,
  3
]
Example (Last)

Get the last (best) solutions from the Generate example.

package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewVar(s, 0)
	s = s.Generate(func(s store.Store) store.Generator {
		value := x.Get(s)
		return store.Lazy(
			func() bool {
				return value <= 2
			},
			func() store.Store {
				value++
				return s.Apply(x.Set(value))
			},
		)
	})

	solver := s.
		Value(func(s store.Store) int { return x.Get(s) }).
		Format(func(s store.Store) any { return x.Get(s) }).
		Maximizer(store.DefaultOptions())

	// Get the last solution of the problem and print it.
	last := solver.Last(context.Background())
	b, err := json.MarshalIndent(last.Store, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
}
Output:

3

type Statistics deprecated

type Statistics struct {
	// Bounds of the store. Nil when using a Satisfier.
	Bounds *Bounds `json:"bounds,omitempty"`
	Search Search  `json:"search"`
	Time   Time    `json:"time"`
	// Value of the store. Nil when using a Satisfier.
	Value *int `json:"value,omitempty"`
}

Statistics of the search.

Deprecated: This package is deprecated and will be removed in the next major release.

type Store deprecated

type Store interface {
	/*
		Apply changes to a Store. A change happens when a stored variable is
		updated:

			s := store.New()
			x := store.NewVar(s, 3.1416)
			s1 := s.Apply(
				x.Set(x.Get(s) * 2),
			)

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Apply(...Change) Store

	/*
		Bound the value of a Store. The solver can use this information to more
		efficiently find the best Store. The lower and upper bounds can be set:

			s := store.New()
			x := store.NewVar(s, initial)
			s = s.Bound(func(s store.Store) store.Bounds {
				return store.Bounds{
					Lower: -1,
					Upper: 1,
				}
			})

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Bound(Bounder) Store

	/*
		Format a Store into any structure prior to JSON encoding.

			s := store.New()
			x := store.NewVar(s, 10)
			s = s.Format(func(s store.Store) any {
				return map[string]int{"x": x.Get(s)}
			})

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Format(Formatter) Store

	/*
		Generate new Stores (children) from the existing one (parent). A
		callback function provides a lexical scope that can be used to perform
		and update calculations.

			s := store.New()
			x := store.NewVar(s, 0)
			s = s.Generate(func(s store.Store) store.Generator {
				value := x.Get(s)
				return store.Lazy(
					func() bool {
						return value <= 2
					},
					func() store.Store {
						value++
						return s.Apply(x.Set(value))
					},
				)
			})

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Generate(func(Store) Generator) Store

	/*
		Propagate changes into a Store and is re-invoked until no further
		changes need to be made and an empty slice of changes is returned.

			s := store.New()
			x := store.NewVar(s, 1)
			s = s.Propagate(func(s store.Store) []store.Change {
				if x.Get(s) <= 1 {
					return []store.Change{
						x.Set(2),
						x.Set(42),
					}
				}
				return []store.Change{}
			})

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Propagate(...Propagator) Store

	/*
		Validate the Store. A Store is operationally valid if all decisions
		have been made and those decisions fulfill certain requirements; e.g.:
		all stops have been assigned to vehicles, all shifts are covered with
		the necessary personnel, all assignment have been made, quantity
		respects an alloted capacity, etc. Setting operational validity is
		optional and the default is true.

			s := store.New()
			x := store.NewVar(s, 1)
			s = s.Validate(func(s store.Store) bool {
				return x.Get(s)%2 == 0
			})

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Validate(Condition) Store

	/*
		Value sets the integer value of a Store. When maximizing or minimizing,
		this is the value that is optimized.

			s := store.New()
			x := store.NewVar(s, 6)
			s = s.Value(func(s store.Store) int {
				v := x.Get(s)
				return v * v
			})

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Value(Valuer) Store

	// Maximizer builds a solver that searches the space defined by the Store
	// to maximize a value.
	//
	// Deprecated: This package is deprecated and will be removed in the next major release.
	Maximizer(Options) Solver

	// Minimizer builds a solver that searches the space defined by the Store
	// to minimize a value.
	//
	// Deprecated: This package is deprecated and will be removed in the next major release.
	Minimizer(Options) Solver

	// Satisfier builds a solver that searches the space defined by the Store
	// to satisfy operational validity.
	//
	// Deprecated: This package is deprecated and will be removed in the next major release.
	Satisfier(Options) Solver
}

Store represents a store of variables and logic to solve decision automation problems. Adding logic to the Store updates it (functions may be called directly and chained):

s := store.New()    // s := store.New().
s = s.Apply(...)    // 	   Apply(...).
s = s.Bound(...)    // 	   Bound(...).
s = s.Format(...)   // 	   Format(...).
s = s.Generate(...) // 	   Generate(...)

The variables and logic stored define a solution space. This space is searched to make decisions.

Deprecated: This package is deprecated and will be removed in the next major release.

Example (Apply)

Applying changes to a store updates it, e.g.: setting the value of a variable.

package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewVar(s, 3.1416)
	s1 := s.Apply(
		x.Set(x.Get(s) * 2),
	)

	fmt.Println(x.Get(s))
	fmt.Println(x.Get(s1))
}
Output:

3.1416
6.2832
Example (Bound)

Make an initial value approach a target by minimizing the absolute difference between them. The store is bounded near zero to help the solver look for the best solution. The resulting bounds are tightened.

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"math"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	initial := 10
	target := 16
	x := store.NewVar(s, initial)
	s = s.Bound(func(s store.Store) store.Bounds {
		return store.Bounds{
			Lower: -1,
			Upper: 1,
		}
	})

	solver := s.
		Value(func(s store.Store) int {
			diff := float64(target - x.Get(s))
			return int(math.Abs(diff))
		}).
		Generate(func(s store.Store) store.Generator {
			value := x.Get(s)
			return store.Lazy(
				func() bool {
					return value <= 2*target
				},
				func() store.Store {
					value++
					return s.Apply(x.Set(value))
				},
			)
		}).
		Format(func(s store.Store) any { return x.Get(s) }).
		Minimizer(store.DefaultOptions())

	// Get the last solution of the problem and print it.
	last := solver.Last(context.Background())

	// Override this variable to have a consistent testable example.
	last.Statistics.Time = store.Time{}

	b, err := json.MarshalIndent(last, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
}
Output:

{
  "store": 16,
  "statistics": {
    "bounds": {
      "lower": -1,
      "upper": 0
    },
    "search": {
      "generated": 6,
      "filtered": 0,
      "expanded": 6,
      "reduced": 0,
      "restricted": 6,
      "deferred": 6,
      "explored": 0,
      "solutions": 7
    },
    "time": {
      "elapsed": "0s",
      "elapsed_seconds": 0,
      "start": "0001-01-01T00:00:00Z"
    },
    "value": 0
  }
}
Example (Format)

A store can be formatted to any JSON representation.

package main

import (
	"encoding/json"
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewVar(s, 10)
	s = s.Format(func(s store.Store) any {
		return map[string]int{"x": x.Get(s)}
	})

	b, err := json.Marshal(s)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
}
Output:

{"x":10}
Example (Generate)

Given a parent, which is simply an integer variable, children are generated by adding 1. This is done until the value reaches a certain limit.

package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewVar(s, 0)
	s = s.Generate(func(s store.Store) store.Generator {
		value := x.Get(s)
		return store.Lazy(
			func() bool {
				return value <= 2
			},
			func() store.Store {
				value++
				return s.Apply(x.Set(value))
			},
		)
	})

	solver := s.
		Value(func(s store.Store) int { return x.Get(s) }).
		Format(func(s store.Store) any { return x.Get(s) }).
		Maximizer(store.DefaultOptions())

	// Get the last solution of the problem and print it.
	last := solver.Last(context.Background())
	b, err := json.MarshalIndent(last.Store, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
}
Output:

3
Example (Maximizer)

Increase the value of a variable as much as possible.

package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewVar(s, 10)
	maximizer := s.
		Value(x.Get).
		Format(func(s store.Store) any { return x.Get(s) }).
		Generate(func(s store.Store) store.Generator {
			value := x.Get(s)
			return store.Lazy(
				func() bool { return value <= 20 },
				func() store.Store {
					value += 5
					return s.Apply(x.Set(value))
				},
			)
		}).
		Maximizer(store.DefaultOptions())

	// Get the last solution of the problem and print it.
	last := maximizer.Last(context.Background())
	b, err := json.MarshalIndent(last.Store, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
}
Output:

25
Example (Minimizer)

Decrease the value of a variable as much as possible.

package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewVar(s, 10)
	minimizer := s.
		Value(x.Get).
		Format(func(s store.Store) any { return x.Get(s) }).
		Generate(func(s store.Store) store.Generator {
			value := x.Get(s)
			return store.Lazy(
				func() bool { return value >= 0 },
				func() store.Store {
					value -= 5
					return s.Apply(x.Set(value))
				},
			)
		}).
		Minimizer(store.DefaultOptions())

	// Get the last solution of the problem and print it.
	last := minimizer.Last(context.Background())
	b, err := json.MarshalIndent(last.Store, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
}
Output:

-5
Example (Satisfier)

Find the first number divisible by 6, starting from 100.

package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewVar(s, 100)
	opt := store.DefaultOptions()
	opt.Limits.Solutions = 1
	opt.Diagram.Expansion.Limit = 1
	satisfier := s.
		Format(func(s store.Store) any { return x.Get(s) }).
		Validate(func(s store.Store) bool {
			return x.Get(s)%6 == 0
		}).
		Generate(func(s store.Store) store.Generator {
			value := x.Get(s)
			return store.Lazy(
				func() bool {
					return value > 0
				},
				func() store.Store {
					value--
					return s.Apply(x.Set(value))
				},
			)
		}).
		Satisfier(opt)

	// Get the last solution of the problem and print it.
	last := satisfier.Last(context.Background())
	b, err := json.MarshalIndent(last.Store, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
}
Output:

96
Example (Validate)

Validating that 1 is divisible by 2 results in an operational invalid store, represented as null.

package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewVar(s, 1)
	s = s.Validate(func(s store.Store) bool {
		return x.Get(s)%2 == 0
	})

	solver := s.
		Format(func(s store.Store) any { return x.Get(s) }).
		Satisfier(store.DefaultOptions())

	// Get the last solution of the problem and print it.
	last := solver.Last(context.Background())
	b, err := json.MarshalIndent(last.Store, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
}
Output:

null
Example (Value)

A custom value can be set on a store. Using any solver, the store has the given value.

package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewVar(s, 6)
	s = s.Value(func(s store.Store) int {
		v := x.Get(s)
		return v * v
	})

	solver := s.Minimizer(store.DefaultOptions())

	// Get the last solution of the problem and print it.
	last := solver.Last(context.Background())
	b, err := json.MarshalIndent(last.Statistics.Value, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
}
Output:

36

func New deprecated

func New() Store

New returns a new Store.

Deprecated: This package is deprecated and will be removed in the next major release.

type Time deprecated

type Time struct {
	Start   time.Time     `json:"start"`
	Elapsed time.Duration `json:"elapsed"`
}

Time statistics.

Deprecated: This package is deprecated and will be removed in the next major release.

func (Time) MarshalJSON deprecated

func (t Time) MarshalJSON() ([]byte, error)

MarshalJSON Time.

Deprecated: This package is deprecated and will be removed in the next major release.

type Valuer deprecated

type Valuer func(Store) int

Valuer maps a Store to an integer value. It is meant to be used with the store.Value function.

Deprecated: This package is deprecated and will be removed in the next major release.

type Var deprecated

type Var[T any] interface {
	/*
		Get the current value of the variable in the Store.

			s := store.New()
			x := store.NewVar(s, 10)
			s = s.Format(func(s store.Store) any {
				return map[string]int{"x": x.Get(s)}
			})

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Get(Store) T

	/*
		Set a new value on the variable.

			s := store.New()
			x := store.NewVar(s, 10)
			s = s.Apply(x.Set(15))

		Deprecated: This package is deprecated and will be removed in the next major release.
	*/
	Set(T) Change
}

Var is a variable stored in a Store.

Deprecated: This package is deprecated and will be removed in the next major release.

Example (Get)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewVar(s, 10)
	fmt.Println(x.Get(s))
}
Output:

10
Example (Set)
package main

import (
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewVar(s, 10)
	fmt.Println(x.Get(s))
	s1 := s.Apply(x.Set(15))
	fmt.Println(x.Get(s1))
}
Output:

10
15

func NewVar deprecated

func NewVar[T any](s Store, data T) Var[T]

NewVar stores a new variable in a Store.

s := store.New()
x := store.NewVar(s, 10) // x is stored in s.

Deprecated: This package is deprecated and will be removed in the next major release.

Example

Declaring a new variable adds the variable to the store.

package main

import (
	"encoding/json"
	"fmt"

	"github.com/nextmv-io/sdk/store"
)

func main() {
	s := store.New()
	x := store.NewVar(s, 10)
	s = s.Apply(x.Set(15))
	b, err := json.Marshal(s)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
}
Output:

[15]

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL