// Copyright 2022 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.

package abt

import (
	"fmt"
	"strconv"
	"strings"
)

const (
	LEAF_HEIGHT = 1
	ZERO_HEIGHT = 0
	NOT_KEY32   = int32(-0x80000000)
)

// T is the exported applicative balanced tree data type.
// A T can be used as a value; updates to one copy of the value
// do not change other copies.
type T struct {
	root *node32
	size int
}

// node32 is the internal tree node data type
type node32 struct {
	// Standard conventions hold for left = smaller, right = larger
	left, right *node32
	data        any
	key         int32
	height_     int8
}

func makeNode(key int32) *node32 {
	return &node32{key: key, height_: LEAF_HEIGHT}
}

// IsEmpty returns true iff t is empty.
func (t *T) IsEmpty() bool {
	return t.root == nil
}

// IsSingle returns true iff t is a singleton (leaf).
func (t *T) IsSingle() bool {
	return t.root != nil && t.root.isLeaf()
}

// VisitInOrder applies f to the key and data pairs in t,
// with keys ordered from smallest to largest.
func (t *T) VisitInOrder(f func(int32, any)) {
	if t.root == nil {
		return
	}
	t.root.visitInOrder(f)
}

func (n *node32) nilOrData() any {
	if n == nil {
		return nil
	}
	return n.data
}

func (n *node32) nilOrKeyAndData() (k int32, d any) {
	if n == nil {
		k = NOT_KEY32
		d = nil
	} else {
		k = n.key
		d = n.data
	}
	return
}

func (n *node32) height() int8 {
	if n == nil {
		return 0
	}
	return n.height_
}

// Find returns the data associated with x in the tree, or
// nil if x is not in the tree.
func (t *T) Find(x int32) any {
	return t.root.find(x).nilOrData()
}

// Insert either adds x to the tree if x was not previously
// a key in the tree, or updates the data for x in the tree if
// x was already a key in the tree.  The previous data associated
// with x is returned, and is nil if x was not previously a
// key in the tree.
func (t *T) Insert(x int32, data any) any {
	if x == NOT_KEY32 {
		panic("Cannot use sentinel value -0x80000000 as key")
	}
	n := t.root
	var newroot *node32
	var o *node32
	if n == nil {
		n = makeNode(x)
		newroot = n
	} else {
		newroot, n, o = n.aInsert(x)
	}
	var r any
	if o != nil {
		r = o.data
	} else {
		t.size++
	}
	n.data = data
	t.root = newroot
	return r
}

func (t *T) Copy() *T {
	u := *t
	return &u
}

func (t *T) Delete(x int32) any {
	n := t.root
	if n == nil {
		return nil
	}
	d, s := n.aDelete(x)
	if d == nil {
		return nil
	}
	t.root = s
	t.size--
	return d.data
}

func (t *T) DeleteMin() (int32, any) {
	n := t.root
	if n == nil {
		return NOT_KEY32, nil
	}
	d, s := n.aDeleteMin()
	if d == nil {
		return NOT_KEY32, nil
	}
	t.root = s
	t.size--
	return d.key, d.data
}

func (t *T) DeleteMax() (int32, any) {
	n := t.root
	if n == nil {
		return NOT_KEY32, nil
	}
	d, s := n.aDeleteMax()
	if d == nil {
		return NOT_KEY32, nil
	}
	t.root = s
	t.size--
	return d.key, d.data
}

func (t *T) Size() int {
	return t.size
}

// Intersection returns the intersection of t and u, where the result
// data for any common keys is given by f(t's data, u's data) -- f need
// not be symmetric.  If f returns nil, then the key and data are not
// added to the result.  If f itself is nil, then whatever value was
// already present in the smaller set is used.
func (t *T) Intersection(u *T, f func(x, y any) any) *T {
	if t.Size() == 0 || u.Size() == 0 {
		return &T{}
	}

	// For faster execution and less allocation, prefer t smaller, iterate over t.
	if t.Size() <= u.Size() {
		v := t.Copy()
		for it := t.Iterator(); !it.Done(); {
			k, d := it.Next()
			e := u.Find(k)
			if e == nil {
				v.Delete(k)
				continue
			}
			if f == nil {
				continue
			}
			if c := f(d, e); c != d {
				if c == nil {
					v.Delete(k)
				} else {
					v.Insert(k, c)
				}
			}
		}
		return v
	}
	v := u.Copy()
	for it := u.Iterator(); !it.Done(); {
		k, e := it.Next()
		d := t.Find(k)
		if d == nil {
			v.Delete(k)
			continue
		}
		if f == nil {
			continue
		}
		if c := f(d, e); c != d {
			if c == nil {
				v.Delete(k)
			} else {
				v.Insert(k, c)
			}
		}
	}

	return v
}

// Union returns the union of t and u, where the result data for any common keys
// is given by f(t's data, u's data) -- f need not be symmetric.  If f returns nil,
// then the key and data are not added to the result.  If f itself is nil, then
// whatever value was already present in the larger set is used.
func (t *T) Union(u *T, f func(x, y any) any) *T {
	if t.Size() == 0 {
		return u
	}
	if u.Size() == 0 {
		return t
	}

	if t.Size() >= u.Size() {
		v := t.Copy()
		for it := u.Iterator(); !it.Done(); {
			k, e := it.Next()
			d := t.Find(k)
			if d == nil {
				v.Insert(k, e)
				continue
			}
			if f == nil {
				continue
			}
			if c := f(d, e); c != d {
				if c == nil {
					v.Delete(k)
				} else {
					v.Insert(k, c)
				}
			}
		}
		return v
	}

	v := u.Copy()
	for it := t.Iterator(); !it.Done(); {
		k, d := it.Next()
		e := u.Find(k)
		if e == nil {
			v.Insert(k, d)
			continue
		}
		if f == nil {
			continue
		}
		if c := f(d, e); c != d {
			if c == nil {
				v.Delete(k)
			} else {
				v.Insert(k, c)
			}
		}
	}
	return v
}

// Difference returns the difference of t and u, subject to the result
// of f applied to data corresponding to equal keys.  If f returns nil
// (or if f is nil) then the key+data are excluded, as usual.  If f
// returns not-nil, then that key+data pair is inserted. instead.
func (t *T) Difference(u *T, f func(x, y any) any) *T {
	if t.Size() == 0 {
		return &T{}
	}
	if u.Size() == 0 {
		return t
	}
	v := t.Copy()
	for it := t.Iterator(); !it.Done(); {
		k, d := it.Next()
		e := u.Find(k)
		if e != nil {
			if f == nil {
				v.Delete(k)
				continue
			}
			c := f(d, e)
			if c == nil {
				v.Delete(k)
				continue
			}
			if c != d {
				v.Insert(k, c)
			}
		}
	}
	return v
}

func (t *T) Iterator() Iterator {
	return Iterator{it: t.root.iterator()}
}

func (t *T) Equals(u *T) bool {
	if t == u {
		return true
	}
	if t.Size() != u.Size() {
		return false
	}
	return t.root.equals(u.root)
}

func (t *T) String() string {
	var b strings.Builder
	first := true
	for it := t.Iterator(); !it.Done(); {
		k, v := it.Next()
		if first {
			first = false
		} else {
			b.WriteString("; ")
		}
		b.WriteString(strconv.FormatInt(int64(k), 10))
		b.WriteString(":")
		fmt.Fprint(&b, v)
	}
	return b.String()
}

func (t *node32) equals(u *node32) bool {
	if t == u {
		return true
	}
	it, iu := t.iterator(), u.iterator()
	for !it.done() && !iu.done() {
		nt := it.next()
		nu := iu.next()
		if nt == nu {
			continue
		}
		if nt.key != nu.key {
			return false
		}
		if nt.data != nu.data {
			return false
		}
	}
	return it.done() == iu.done()
}

func (t *T) Equiv(u *T, eqv func(x, y any) bool) bool {
	if t == u {
		return true
	}
	if t.Size() != u.Size() {
		return false
	}
	return t.root.equiv(u.root, eqv)
}

func (t *node32) equiv(u *node32, eqv func(x, y any) bool) bool {
	if t == u {
		return true
	}
	it, iu := t.iterator(), u.iterator()
	for !it.done() && !iu.done() {
		nt := it.next()
		nu := iu.next()
		if nt == nu {
			continue
		}
		if nt.key != nu.key {
			return false
		}
		if !eqv(nt.data, nu.data) {
			return false
		}
	}
	return it.done() == iu.done()
}

type iterator struct {
	parents []*node32
}

type Iterator struct {
	it iterator
}

func (it *Iterator) Next() (int32, any) {
	x := it.it.next()
	if x == nil {
		return NOT_KEY32, nil
	}
	return x.key, x.data
}

func (it *Iterator) Done() bool {
	return len(it.it.parents) == 0
}

func (t *node32) iterator() iterator {
	if t == nil {
		return iterator{}
	}
	it := iterator{parents: make([]*node32, 0, int(t.height()))}
	it.leftmost(t)
	return it
}

func (it *iterator) leftmost(t *node32) {
	for t != nil {
		it.parents = append(it.parents, t)
		t = t.left
	}
}

func (it *iterator) done() bool {
	return len(it.parents) == 0
}

func (it *iterator) next() *node32 {
	l := len(it.parents)
	if l == 0 {
		return nil
	}
	x := it.parents[l-1] // return value
	if x.right != nil {
		it.leftmost(x.right)
		return x
	}
	// discard visited top of parents
	l--
	it.parents = it.parents[:l]
	y := x // y is known visited/returned
	for l > 0 && y == it.parents[l-1].right {
		y = it.parents[l-1]
		l--
		it.parents = it.parents[:l]
	}

	return x
}

// Min returns the minimum element of t.
// If t is empty, then (NOT_KEY32, nil) is returned.
func (t *T) Min() (k int32, d any) {
	return t.root.min().nilOrKeyAndData()
}

// Max returns the maximum element of t.
// If t is empty, then (NOT_KEY32, nil) is returned.
func (t *T) Max() (k int32, d any) {
	return t.root.max().nilOrKeyAndData()
}

// Glb returns the greatest-lower-bound-exclusive of x and the associated
// data.  If x has no glb in the tree, then (NOT_KEY32, nil) is returned.
func (t *T) Glb(x int32) (k int32, d any) {
	return t.root.glb(x, false).nilOrKeyAndData()
}

// GlbEq returns the greatest-lower-bound-inclusive of x and the associated
// data.  If x has no glbEQ in the tree, then (NOT_KEY32, nil) is returned.
func (t *T) GlbEq(x int32) (k int32, d any) {
	return t.root.glb(x, true).nilOrKeyAndData()
}

// Lub returns the least-upper-bound-exclusive of x and the associated
// data.  If x has no lub in the tree, then (NOT_KEY32, nil) is returned.
func (t *T) Lub(x int32) (k int32, d any) {
	return t.root.lub(x, false).nilOrKeyAndData()
}

// LubEq returns the least-upper-bound-inclusive of x and the associated
// data.  If x has no lubEq in the tree, then (NOT_KEY32, nil) is returned.
func (t *T) LubEq(x int32) (k int32, d any) {
	return t.root.lub(x, true).nilOrKeyAndData()
}

func (t *node32) isLeaf() bool {
	return t.left == nil && t.right == nil && t.height_ == LEAF_HEIGHT
}

func (t *node32) visitInOrder(f func(int32, any)) {
	if t.left != nil {
		t.left.visitInOrder(f)
	}
	f(t.key, t.data)
	if t.right != nil {
		t.right.visitInOrder(f)
	}
}

func (t *node32) find(key int32) *node32 {
	for t != nil {
		if key < t.key {
			t = t.left
		} else if key > t.key {
			t = t.right
		} else {
			return t
		}
	}
	return nil
}

func (t *node32) min() *node32 {
	if t == nil {
		return t
	}
	for t.left != nil {
		t = t.left
	}
	return t
}

func (t *node32) max() *node32 {
	if t == nil {
		return t
	}
	for t.right != nil {
		t = t.right
	}
	return t
}

func (t *node32) glb(key int32, allow_eq bool) *node32 {
	var best *node32 = nil
	for t != nil {
		if key <= t.key {
			if allow_eq && key == t.key {
				return t
			}
			// t is too big, glb is to left.
			t = t.left
		} else {
			// t is a lower bound, record it and seek a better one.
			best = t
			t = t.right
		}
	}
	return best
}

func (t *node32) lub(key int32, allow_eq bool) *node32 {
	var best *node32 = nil
	for t != nil {
		if key >= t.key {
			if allow_eq && key == t.key {
				return t
			}
			// t is too small, lub is to right.
			t = t.right
		} else {
			// t is an upper bound, record it and seek a better one.
			best = t
			t = t.left
		}
	}
	return best
}

func (t *node32) aInsert(x int32) (newroot, newnode, oldnode *node32) {
	// oldnode default of nil is good, others should be assigned.
	if x == t.key {
		oldnode = t
		newt := *t
		newnode = &newt
		newroot = newnode
		return
	}
	if x < t.key {
		if t.left == nil {
			t = t.copy()
			n := makeNode(x)
			t.left = n
			newnode = n
			newroot = t
			t.height_ = 2 // was balanced w/ 0, sibling is height 0 or 1
			return
		}
		var new_l *node32
		new_l, newnode, oldnode = t.left.aInsert(x)
		t = t.copy()
		t.left = new_l
		if new_l.height() > 1+t.right.height() {
			newroot = t.aLeftIsHigh(newnode)
		} else {
			t.height_ = 1 + max(t.left.height(), t.right.height())
			newroot = t
		}
	} else { // x > t.key
		if t.right == nil {
			t = t.copy()
			n := makeNode(x)
			t.right = n
			newnode = n
			newroot = t
			t.height_ = 2 // was balanced w/ 0, sibling is height 0 or 1
			return
		}
		var new_r *node32
		new_r, newnode, oldnode = t.right.aInsert(x)
		t = t.copy()
		t.right = new_r
		if new_r.height() > 1+t.left.height() {
			newroot = t.aRightIsHigh(newnode)
		} else {
			t.height_ = 1 + max(t.left.height(), t.right.height())
			newroot = t
		}
	}
	return
}

func (t *node32) aDelete(key int32) (deleted, newSubTree *node32) {
	if t == nil {
		return nil, nil
	}

	if key < t.key {
		oh := t.left.height()
		d, tleft := t.left.aDelete(key)
		if tleft == t.left {
			return d, t
		}
		return d, t.copy().aRebalanceAfterLeftDeletion(oh, tleft)
	} else if key > t.key {
		oh := t.right.height()
		d, tright := t.right.aDelete(key)
		if tright == t.right {
			return d, t
		}
		return d, t.copy().aRebalanceAfterRightDeletion(oh, tright)
	}

	if t.height() == LEAF_HEIGHT {
		return t, nil
	}

	// Interior delete by removing left.Max or right.Min,
	// then swapping contents
	if t.left.height() > t.right.height() {
		oh := t.left.height()
		d, tleft := t.left.aDeleteMax()
		r := t
		t = t.copy()
		t.data, t.key = d.data, d.key
		return r, t.aRebalanceAfterLeftDeletion(oh, tleft)
	}

	oh := t.right.height()
	d, tright := t.right.aDeleteMin()
	r := t
	t = t.copy()
	t.data, t.key = d.data, d.key
	return r, t.aRebalanceAfterRightDeletion(oh, tright)
}

func (t *node32) aDeleteMin() (deleted, newSubTree *node32) {
	if t == nil {
		return nil, nil
	}
	if t.left == nil { // leaf or left-most
		return t, t.right
	}
	oh := t.left.height()
	d, tleft := t.left.aDeleteMin()
	if tleft == t.left {
		return d, t
	}
	return d, t.copy().aRebalanceAfterLeftDeletion(oh, tleft)
}

func (t *node32) aDeleteMax() (deleted, newSubTree *node32) {
	if t == nil {
		return nil, nil
	}

	if t.right == nil { // leaf or right-most
		return t, t.left
	}

	oh := t.right.height()
	d, tright := t.right.aDeleteMax()
	if tright == t.right {
		return d, t
	}
	return d, t.copy().aRebalanceAfterRightDeletion(oh, tright)
}

func (t *node32) aRebalanceAfterLeftDeletion(oldLeftHeight int8, tleft *node32) *node32 {
	t.left = tleft

	if oldLeftHeight == tleft.height() || oldLeftHeight == t.right.height() {
		// this node is still balanced and its height is unchanged
		return t
	}

	if oldLeftHeight > t.right.height() {
		// left was larger
		t.height_--
		return t
	}

	// left height fell by 1 and it was already less than right height
	t.right = t.right.copy()
	return t.aRightIsHigh(nil)
}

func (t *node32) aRebalanceAfterRightDeletion(oldRightHeight int8, tright *node32) *node32 {
	t.right = tright

	if oldRightHeight == tright.height() || oldRightHeight == t.left.height() {
		// this node is still balanced and its height is unchanged
		return t
	}

	if oldRightHeight > t.left.height() {
		// left was larger
		t.height_--
		return t
	}

	// right height fell by 1 and it was already less than left height
	t.left = t.left.copy()
	return t.aLeftIsHigh(nil)
}

// aRightIsHigh does rotations necessary to fix a high right child
// assume that t and t.right are already fresh copies.
func (t *node32) aRightIsHigh(newnode *node32) *node32 {
	right := t.right
	if right.right.height() < right.left.height() {
		// double rotation
		if newnode != right.left {
			right.left = right.left.copy()
		}
		t.right = right.leftToRoot()
	}
	t = t.rightToRoot()
	return t
}

// aLeftIsHigh does rotations necessary to fix a high left child
// assume that t and t.left are already fresh copies.
func (t *node32) aLeftIsHigh(newnode *node32) *node32 {
	left := t.left
	if left.left.height() < left.right.height() {
		// double rotation
		if newnode != left.right {
			left.right = left.right.copy()
		}
		t.left = left.rightToRoot()
	}
	t = t.leftToRoot()
	return t
}

// rightToRoot does that rotation, modifying t and t.right in the process.
func (t *node32) rightToRoot() *node32 {
	//    this
	// left  right
	//      rl   rr
	//
	// becomes
	//
	//       right
	//    this   rr
	// left  rl
	//
	right := t.right
	rl := right.left
	right.left = t
	// parent's child ptr fixed in caller
	t.right = rl
	t.height_ = 1 + max(rl.height(), t.left.height())
	right.height_ = 1 + max(t.height(), right.right.height())
	return right
}

// leftToRoot does that rotation, modifying t and t.left in the process.
func (t *node32) leftToRoot() *node32 {
	//     this
	//  left  right
	// ll  lr
	//
	// becomes
	//
	//    left
	//   ll  this
	//      lr  right
	//
	left := t.left
	lr := left.right
	left.right = t
	// parent's child ptr fixed in caller
	t.left = lr
	t.height_ = 1 + max(lr.height(), t.right.height())
	left.height_ = 1 + max(t.height(), left.left.height())
	return left
}

func (t *node32) copy() *node32 {
	u := *t
	return &u
}
