func (b *Bird) step(items []int) ([]int, []int, error) {
	referrers := make([]int, len(items))
	for i, item := range items {
		relatedUsers := b.ItemsToUsers[item]
		if len(relatedUsers) == 0 {
			return nil, nil, fmt.Errorf("cannot perform step: no one has interacted with item %d", item)
		}
		referrers[i] = relatedUsers[b.RandSource.Intn(len(relatedUsers))]
	}
	newItems := make([]int, len(items))
	for j, user := range referrers {
		newItems[j] = b.sampleItem(user)
	}
	return newItems, referrers, nil
}
step performs one random walk step for each incoming item. It returns a
slice of visited items along with the 'referrers', i.e. the users that were
visited to reach these items.
package birdland
import (
	"fmt"
	"math/rand"
	"time"
	"github.com/pkg/errors"
	"github.com/rlouf/birdland/sampler"
)
type QueryItem struct {
	Item   int
	Weight float64  
}
type BirdCfg struct {
	Depth int `yaml:"depth"`
	Draws int `yaml:"draws"`
}
func NewBirdCfg() *BirdCfg {
	cfg := BirdCfg{
		Depth: 1,
		Draws: 1000,
	}
	return &cfg
}
type Bird struct {
	Cfg               *BirdCfg
	ItemWeights       []float64               
	UsersToItems      [][]int                 
	ItemsToUsers      [][]int                 
	UserItemsSamplers []sampler.AliasSampler  
	RandSource        *rand.Rand
}
func NewBird(cfg *BirdCfg, itemWeights []float64, usersToItems [][]int) (*Bird, error) {
	if cfg.Depth < 1 {
		return nil, errors.New("the depth must be greater than or equal to 1")
	}
	if cfg.Draws < 1 {
		return nil, errors.New("the number of draws must be greater than or equal to 1")
	}
	randSource := rand.New(rand.NewSource(time.Now().UnixNano()))
	err := validateBirdInputs(itemWeights, usersToItems)
	if err != nil {
		return &Bird{}, errors.Wrap(err, "invalid input")
	}
	userItemsSampler, err := initUserItemsSamplers(randSource, itemWeights, usersToItems)
	if err != nil {
		return &Bird{}, errors.Wrap(err, "cannot initialize samplers")
	}
	itemsToUsers := permuteAdjacencyList(len(itemWeights), usersToItems)
	b := Bird{
		Cfg:               cfg,
		RandSource:        randSource,
		ItemWeights:       itemWeights,
		UsersToItems:      usersToItems,
		ItemsToUsers:      itemsToUsers,
		UserItemsSamplers: userItemsSampler,
	}
	return &b, nil
}
func (b *Bird) Process(query []QueryItem) ([]int, []int, error) {
	if len(query) == 0 {
		return nil, nil, errors.New("empty query")
	}
	stepItems, err := b.sampleItemsFromQuery(query)
	if err != nil {
		return nil, nil, errors.Wrap(err, "cannot sample items")
	}
	var items []int
	var referrers []int
	for d := 0; d < b.Cfg.Depth; d++ {
		stepItems, stepReferrers, err := b.step(stepItems)
		if err != nil {
			return nil, nil, errors.Wrap(err, "cannot step through items")
		}
		items = append(items, stepItems...)
		referrers = append(referrers, stepReferrers...)
	}
	return items, referrers, nil
}
func (b *Bird) sampleItemsFromQuery(query []QueryItem) ([]int, error) {
	weights := make([]float64, len(query))
	items := make([]int, len(query))
	for i, q := range query {
		weights[i] = q.Weight * b.ItemWeights[q.Item]
		items[i] = q.Item
	}
	s, err := sampler.NewAliasSampler(b.RandSource, weights)
	if err != nil {
		return nil, errors.Wrap(err, "cannot create sampler")
	}
	sampledItems := make([]int, b.Cfg.Draws)
	for i, iid := range s.Sample(b.Cfg.Draws) {
		if len(b.ItemsToUsers[items[iid]]) == 0 {
			continue
		}
		sampledItems[i] = items[iid]
	}
	if len(sampledItems) == 0 {
		return nil, errors.New("no items were sampled," +
			"check that the query refers to actual items.")
	}
	return sampledItems, nil
}