package main

import (
	"fmt"
	"math/bits"
)

func majorityIntersections(n int) (pairs, disjoint int) {
	if n < 1 || n > 10 {
		panic("bounded model: 1..10 voters")
	}
	q := n/2 + 1
	for a := 1; a < 1<<n; a++ {
		if bits.OnesCount(uint(a)) != q {
			continue
		}
		for b := 1; b < 1<<n; b++ {
			if bits.OnesCount(uint(b)) != q {
				continue
			}
			pairs++
			if a&b == 0 {
				disjoint++
			}
		}
	}
	return
}

type Tail struct{ Term, Index int }

func upToDate(candidate, voter Tail) bool {
	return candidate.Term > voter.Term || candidate.Term == voter.Term && candidate.Index >= voter.Index
}

// Tests only the leader's direct commit predicate, not all Raft safety rules.
func directCommit(entryTerm, currentTerm, replicas, voters int) bool {
	return entryTerm == currentTerm && replicas > voters/2
}

type Vote struct {
	Term      int
	Candidate string
}

func (v *Vote) grant(term int, candidate string) bool {
	if term < v.Term {
		return false
	}
	if term > v.Term {
		v.Term = term
		v.Candidate = ""
	}
	if v.Candidate != "" && v.Candidate != candidate {
		return false
	}
	v.Candidate = candidate
	return true
}
func main() {
	p, d := majorityIntersections(5)
	fmt.Println("5 voters: quorum pairs =", p, "disjoint =", d)
	fmt.Println("term 2 entry in term 4, 3/5:", directCommit(2, 4, 3, 5))
	fmt.Println("term 4 entry in term 4, 3/5:", directCommit(4, 4, 3, 5))
	fmt.Println("longer but older tail:", upToDate(Tail{2, 100}, Tail{3, 10}))
	v := Vote{}
	fmt.Println("same term A then B:", v.grant(7, "A"), v.grant(7, "B"))
	// Incorrect restart: a new zero value models losing the persisted vote.
	v = Vote{}
	fmt.Println("lost vote after restart, B:", v.grant(7, "B"))
}
