package main

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

var Channels, ChannelSize int = 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( chans [] chan uint64 ) {
	var count uint64 = 0
	for {
		if cons_done { break }		
		<-chans[1]
		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( chans [] chan uint64 ) {
	var count uint64 = 0
	var checksum uint64 = 0
	for {
		if prod_done { break }
		checksum = checksum ^ count
		chans[1] <- count
		count++
	}
	prodJoin <- 0
}


func main() {
	runtime.GOMAXPROCS( 4 );

	// 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)
	}

	
	go selectproducer( chans )
	go selectconsumer( chans )
	go producer( chans )
	go consumer( chans )
	

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