package main

import (
	"fmt"
	"sync"
	"time"
	"runtime"
	"os"
	"strconv"
)

var Sets, Channels, ChannelSize int = 1, 2, 100
var cons_done, prod_done bool = false, false;
var total_operations uint64 = 0
var m sync.Mutex

var prodJoin chan int = make(chan int)
var consJoin chan int = make(chan int)

func selectconsumer( chans [] chan uint64 ) {
	var count uint64 = 0
	for {
		if cons_done { break }		
		select {
			case <- chans[0]:
			case <- chans[1]:
		}
		if ! prod_done { count++ }
	}
	m.Lock()
	total_operations += count
	m.Unlock()
	consJoin <- 0
}

func consumer( channel chan uint64 ) {
	var count uint64 = 0
	for {
		if cons_done { break }
		<-channel
		if ! prod_done { count++ }
	}
	m.Lock()
	total_operations += count
	m.Unlock()
	consJoin <- 0
}

func selectproducer( chans [] chan uint64 ) {
	var count uint64 = 0
	var checksum uint64 = 0
	for {
		if prod_done { break }
		checksum = checksum ^ count
		select {
			case chans[0] <- count:
			case chans[1] <- count:
		}
		count++
	}
	prodJoin <- 0
}

func producer( channel chan uint64 ) {
	var count uint64 = 0
	for {
		if prod_done { break }
		channel <- count
		count++
	}
	prodJoin <- 0
}

func usage() {
	fmt.Printf( "Usage: %v " +
		"[ sets (> 0) | 'd' (default %v) ] " +
		"[ ChannelSize (> 0) | 'd' (default %v) ]\n",
		os.Args[0], Sets, ChannelSize );
	os.Exit( 1 );
}

func main() {
	switch len( os.Args ) {
		case 3:
			if os.Args[2] != "d" {							// default ?
				ChannelSize, _ = strconv.Atoi( os.Args[2] )
					if ChannelSize < 0 { usage(); }
			} // if
		fallthrough
		case 2:
			if os.Args[1] != "d" {							// default ?
				Sets, _ = strconv.Atoi( os.Args[1] )
				if Sets < 1 { usage(); }
			} // if
		case 1:											// use defaults
		default:
		usage();
	} // switch
	runtime.GOMAXPROCS( Sets * 2 + Sets * Channels * 2 );

	// fmt.Println("Processors: ",Processors," Channels: ",Channels," ProdsPerChan: ",ProdsPerChan," ConsPerChan: ",ConsPerChan," Channel Size: ",ChannelSize)
	
	chans := make( [] chan uint64, Channels )
	for i := range chans {
		chans[i] = make(chan uint64, ChannelSize)
	}

	
	for j := 0; j < Sets; j++ {
		go selectproducer( chans )
		go selectconsumer( chans )
		for i := 0; i < Channels; i++ {
			go producer( chans[i] )
			go consumer( chans[i] )
		}
	}
		

	// wait 10 seconds
	time.Sleep(time.Second * 10)
	// fmt.Println("prod done\n")
	prod_done = true
	for j := 0; j < Sets + Sets * Channels; j++ {
		<-prodJoin
	}
	// fmt.Println("cons done\n")
	cons_done = true
	for i := range chans {
		close(chans[i])
	}
	
	for j := 0; j < Sets + Sets * Channels; j++{
		<-consJoin
	}
    fmt.Println(total_operations)
}