func (groups LogPolicyData) TotalLogs() int {
	unifiedLogs := make(map[string]bool)
	for _, g := range groups {
		if g.IsBase {
			return len(g.LogURLs)
		}
		for l := range g.LogURLs {
			unifiedLogs[l] = true
		}
	}
	return len(unifiedLogs)
}
TotalLogs returns number of logs within set of Log-groups.
Taking possible intersection into account.
package ctpolicy
import (
	"fmt"
	"sync"
	"github.com/google/certificate-transparency-go/loglist2"
	"github.com/google/certificate-transparency-go/x509"
)
const (
	BaseName = "All-logs"
)
type LogGroupInfo struct {
	Name          string
	LogURLs       map[string]bool     
	MinInclusions int                 
	IsBase        bool                
	LogWeights    map[string]float32  
	wMu           sync.RWMutex        
}
func (group *LogGroupInfo) setMinInclusions(i int) error {
	if i < 0 {
		return fmt.Errorf("cannot assign negative minimal inclusions number")
	}
	group.MinInclusions = i
	if i > len(group.LogURLs) {
		return fmt.Errorf("trying to assign %d minimal inclusion number while only %d logs are part of group %q", i, len(group.LogURLs), group.Name)
	}
	return nil
}
func (group *LogGroupInfo) populate(ll *loglist2.LogList, included func(op *loglist2.Operator) bool) {
	group.LogURLs = make(map[string]bool)
	group.LogWeights = make(map[string]float32)
	for _, op := range ll.Operators {
		if included(op) {
			for _, l := range op.Logs {
				group.LogURLs[l.URL] = true
				group.LogWeights[l.URL] = 1.0
			}
		}
	}
}
func (group *LogGroupInfo) satisfyMinimalInclusion(weights map[string]float32) bool {
	nonZeroNum := 0
	for logURL, w := range weights {
		if group.LogURLs[logURL] && w > 0.0 {
			nonZeroNum++
			if nonZeroNum >= group.MinInclusions {
				return true
			}
		}
	}
	return false
}
func (group *LogGroupInfo) SetLogWeights(weights map[string]float32) error {
	for logURL, w := range weights {
		if w < 0.0 {
			return fmt.Errorf("trying to assign negative weight %v to Log %q", w, logURL)
		}
	}
	if !group.satisfyMinimalInclusion(weights) {
		return fmt.Errorf("trying to assign weights %v resulting in inability to reach minimal inclusion number %d", weights, group.MinInclusions)
	}
	group.wMu.Lock()
	defer group.wMu.Unlock()
	for logURL := range group.LogURLs {
		group.LogWeights[logURL] = 0.0
	}
	for logURL, w := range weights {
		if group.LogURLs[logURL] {
			group.LogWeights[logURL] = w
		}
	}
	return nil
}
func (group *LogGroupInfo) SetLogWeight(logURL string, w float32) error {
	if !group.LogURLs[logURL] {
		return fmt.Errorf("trying to assign weight to Log %q not belonging to the group", logURL)
	}
	if w < 0.0 {
		return fmt.Errorf("trying to assign negative weight %v to Log %q", w, logURL)
	}
	newWeights := make(map[string]float32)
	for l, wt := range group.LogWeights {
		newWeights[l] = wt
	}
	newWeights[logURL] = w
	if !group.satisfyMinimalInclusion(newWeights) {
		return fmt.Errorf("assigning weight %v to Log %q will result in inability to reach minimal inclusion number %d", w, logURL, group.MinInclusions)
	}
	group.wMu.Lock()
	defer group.wMu.Unlock()
	group.LogWeights = newWeights
	return nil
}
func (group *LogGroupInfo) GetSubmissionSession() []string {
	if len(group.LogURLs) == 0 {
		return make([]string, 0)
	}
	session := make([]string, 0)
	unProcessedWeights := make(map[string]float32)
	for logURL, w := range group.LogWeights {
		unProcessedWeights[logURL] = w
	}
	group.wMu.RLock()
	defer group.wMu.RUnlock()
	for range group.LogURLs {
		sampleLog, err := weightedRandomSample(unProcessedWeights)
		if err != nil {
			return session
		}
		session = append(session, sampleLog)
		delete(unProcessedWeights, sampleLog)
	}
	return session
}
type LogPolicyData map[string]*LogGroupInfo