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

// trie implements persistent Patricia trie maps.
//
// Each Map is effectively a map from uint64 to interface{}. Patricia tries are
// a form of radix tree that are particularly appropriate when many maps will be
// created, merged together and large amounts of sharing are expected (e.g.
// environment abstract domains in program analysis).
//
// This implementation closely follows the paper:
//
//	C. Okasaki and A. Gill, “Fast mergeable integer maps,” in ACM SIGPLAN
//	Workshop on ML, September 1998, pp. 77–86.
//
// Each Map is immutable and can be read from concurrently. The map does not
// guarantee that the value pointed to by the interface{} value is not updated
// concurrently.
//
// These Maps are optimized for situations where there will be many maps created at
// with a high degree of sharing and combining of maps together. If you do not expect,
// significant amount of sharing, the builtin map[T]U is much better choice!
//
// Each Map is created by a Builder. Each Builder has a unique Scope and each node is
// created within this scope. Maps x and y are == if they contains the same
// (key,value) mappings and have equal scopes.
//
// Internally these are big endian Patricia trie nodes, and the keys are sorted.
package trie

import (
	"fmt"
	"strings"
)

// Map is effectively a finite mapping from uint64 keys to interface{} values.
// Maps are immutable and can be read from concurrently.
//
// Notes on concurrency:
//   - A Map value itself is an interface and assignments to a Map value can race.
//   - Map does not guarantee that the value pointed to by the interface{} value
//     is not updated concurrently.
type Map struct {
	s Scope
	n node
}

func (m Map) Scope() Scope {
	return m.s
}
func (m Map) Size() int {
	if m.n == nil {
		return 0
	}
	return m.n.size()
}
func (m Map) Lookup(k uint64) (any, bool) {
	if m.n != nil {
		if leaf := m.n.find(key(k)); leaf != nil {
			return leaf.v, true
		}
	}
	return nil, false
}

// Converts the map into a {<key>: <value>[, ...]} string. This uses the default
// %s string conversion for <value>.
func (m Map) String() string {
	var kvs []string
	m.Range(func(u uint64, i any) bool {
		kvs = append(kvs, fmt.Sprintf("%d: %s", u, i))
		return true
	})
	return fmt.Sprintf("{%s}", strings.Join(kvs, ", "))
}

// Range over the leaf (key, value) pairs in the map in order and
// applies cb(key, value) to each. Stops early if cb returns false.
// Returns true if all elements were visited without stopping early.
func (m Map) Range(cb func(uint64, any) bool) bool {
	if m.n != nil {
		return m.n.visit(cb)
	}
	return true
}

// DeepEqual returns true if m and other contain the same (k, v) mappings
// [regardless of Scope].
//
// Equivalently m.DeepEqual(other) <=> reflect.DeepEqual(Elems(m), Elems(other))
func (m Map) DeepEqual(other Map) bool {
	if m.Scope() == other.Scope() {
		return m.n == other.n
	}
	if (m.n == nil) || (other.n == nil) {
		return m.Size() == 0 && other.Size() == 0
	}
	return m.n.deepEqual(other.n)
}

// Elems are the (k,v) elements in the Map as a map[uint64]interface{}
func Elems(m Map) map[uint64]any {
	dest := make(map[uint64]any, m.Size())
	m.Range(func(k uint64, v any) bool {
		dest[k] = v
		return true
	})
	return dest
}

// node is an internal node within a trie map.
// A node is either empty, a leaf or a branch.
type node interface {
	size() int

	// visit the leaves (key, value) pairs in the map in order and
	// applies cb(key, value) to each. Stops early if cb returns false.
	// Returns true if all elements were visited without stopping early.
	visit(cb func(uint64, any) bool) bool

	// Two nodes contain the same elements regardless of scope.
	deepEqual(node) bool

	// find the leaf for the given key value or nil if it is not present.
	find(k key) *leaf

	// implementations must implement this.
	nodeImpl()
}

// empty represents the empty map within a scope.
//
// The current builder ensure
type empty struct {
	s Scope
}

// leaf represents a single <key, value> pair.
type leaf struct {
	k key
	v any
}

// branch represents a tree node within the Patricia trie.
//
// All keys within the branch match a `prefix` of the key
// up to a `branching` bit, and the left and right nodes
// contain keys that disagree on the bit at the `branching` bit.
type branch struct {
	sz        int    // size. cached for O(1) lookup
	prefix    prefix // == mask(p0, branching) for some p0
	branching bitpos

	// Invariants:
	// - neither is nil.
	// - neither is *empty.
	// - all keys in left are <= p.
	// - all keys in right are > p.
	left, right node
}

// all of these types are Maps.
var _ node = &empty{}
var _ node = &leaf{}
var _ node = &branch{}

func (*empty) nodeImpl()  {}
func (*leaf) nodeImpl()   {}
func (*branch) nodeImpl() {}

func (*empty) find(k key) *leaf { return nil }
func (l *leaf) find(k key) *leaf {
	if k == l.k {
		return l
	}
	return nil
}
func (br *branch) find(k key) *leaf {
	kp := prefix(k)
	if !matchPrefix(kp, br.prefix, br.branching) {
		return nil
	}
	if zeroBit(kp, br.branching) {
		return br.left.find(k)
	}
	return br.right.find(k)
}

func (*empty) size() int     { return 0 }
func (*leaf) size() int      { return 1 }
func (br *branch) size() int { return br.sz }

func (*empty) deepEqual(m node) bool {
	_, ok := m.(*empty)
	return ok
}
func (l *leaf) deepEqual(m node) bool {
	if m, ok := m.(*leaf); ok {
		return m == l || (l.k == m.k && l.v == m.v)
	}
	return false
}

func (br *branch) deepEqual(m node) bool {
	if m, ok := m.(*branch); ok {
		if br == m {
			return true
		}
		return br.sz == m.sz && br.branching == m.branching && br.prefix == m.prefix &&
			br.left.deepEqual(m.left) && br.right.deepEqual(m.right)
	}
	// if m is not a branch, m contains 0 or 1 elem.
	// br contains at least 2 keys that disagree on a prefix.
	return false
}

func (*empty) visit(cb func(uint64, any) bool) bool {
	return true
}
func (l *leaf) visit(cb func(uint64, any) bool) bool {
	return cb(uint64(l.k), l.v)
}
func (br *branch) visit(cb func(uint64, any) bool) bool {
	if !br.left.visit(cb) {
		return false
	}
	return br.right.visit(cb)
}
