| //go:build ignore | 
 | // +build ignore | 
 |  | 
 | /* | 
 | Redistribution and use in source and binary forms, with or without | 
 | modification, are permitted provided that the following conditions are met: | 
 |  | 
 |     * Redistributions of source code must retain the above copyright | 
 |     notice, this list of conditions and the following disclaimer. | 
 |  | 
 |     * Redistributions in binary form must reproduce the above copyright | 
 |     notice, this list of conditions and the following disclaimer in the | 
 |     documentation and/or other materials provided with the distribution. | 
 |  | 
 |     * Neither the name of "The Computer Language Benchmarks Game" nor the | 
 |     name of "The Computer Language Shootout Benchmarks" nor the names of | 
 |     its contributors may be used to endorse or promote products derived | 
 |     from this software without specific prior written permission. | 
 |  | 
 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
 | ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | 
 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | 
 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | 
 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | 
 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | 
 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 
 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | 
 | POSSIBILITY OF SUCH DAMAGE. | 
 | */ | 
 |  | 
 | /* | 
 |  * The Computer Language Benchmarks Game | 
 |  * http://shootout.alioth.debian.org/ | 
 |  * | 
 |  * contributed by The Go Authors. | 
 |  * Based on fannkuch.scala by Rex Kerr | 
 |  */ | 
 |  | 
 | package main | 
 |  | 
 | import ( | 
 | 	"flag" | 
 | 	"fmt" | 
 | 	"runtime" | 
 | ) | 
 |  | 
 | var n = flag.Int("n", 7, "count") | 
 | var nCPU = flag.Int("ncpu", 4, "number of cpus") | 
 |  | 
 | type Job struct { | 
 | 	start []int | 
 | 	n     int | 
 | } | 
 |  | 
 | type Found struct { | 
 | 	who *Kucher | 
 | 	k   int | 
 | } | 
 |  | 
 | type Kucher struct { | 
 | 	perm []int | 
 | 	temp []int | 
 | 	flip []int | 
 | 	in   chan Job | 
 | } | 
 |  | 
 | func NewKucher(length int) *Kucher { | 
 | 	return &Kucher{ | 
 | 		perm: make([]int, length), | 
 | 		temp: make([]int, length), | 
 | 		flip: make([]int, length), | 
 | 		in:   make(chan Job), | 
 | 	} | 
 | } | 
 |  | 
 | func (k *Kucher) permute(n int) bool { | 
 | 	i := 0 | 
 | 	for ; i < n-1 && k.flip[i] == 0; i++ { | 
 | 		t := k.perm[0] | 
 | 		j := 0 | 
 | 		for ; j <= i; j++ { | 
 | 			k.perm[j] = k.perm[j+1] | 
 | 		} | 
 | 		k.perm[j] = t | 
 | 	} | 
 | 	k.flip[i]-- | 
 | 	for i > 0 { | 
 | 		i-- | 
 | 		k.flip[i] = i | 
 | 	} | 
 | 	return k.flip[n-1] >= 0 | 
 | } | 
 |  | 
 | func (k *Kucher) count() int { | 
 | 	K := 0 | 
 | 	copy(k.temp, k.perm) | 
 | 	for k.temp[0] != 0 { | 
 | 		m := k.temp[0] | 
 | 		for i := 0; i < m; i++ { | 
 | 			k.temp[i], k.temp[m] = k.temp[m], k.temp[i] | 
 | 			m-- | 
 | 		} | 
 | 		K++ | 
 | 	} | 
 | 	return K | 
 | } | 
 |  | 
 | func (k *Kucher) Run(foreman chan<- Found) { | 
 | 	for job := range k.in { | 
 | 		verbose := 30 | 
 | 		copy(k.perm, job.start) | 
 | 		for i, v := range k.perm { | 
 | 			if v != i { | 
 | 				verbose = 0 | 
 | 			} | 
 | 			k.flip[i] = i | 
 | 		} | 
 | 		K := 0 | 
 | 		for { | 
 | 			if verbose > 0 { | 
 | 				for _, p := range k.perm { | 
 | 					fmt.Print(p + 1) | 
 | 				} | 
 | 				fmt.Println() | 
 | 				verbose-- | 
 | 			} | 
 | 			count := k.count() | 
 | 			if count > K { | 
 | 				K = count | 
 | 			} | 
 | 			if !k.permute(job.n) { | 
 | 				break | 
 | 			} | 
 | 		} | 
 | 		foreman <- Found{k, K} | 
 | 	} | 
 | } | 
 |  | 
 | type Fanner struct { | 
 | 	jobind   int | 
 | 	jobsdone int | 
 | 	k        int | 
 | 	jobs     []Job | 
 | 	workers  []*Kucher | 
 | 	in       chan Found | 
 | 	result   chan int | 
 | } | 
 |  | 
 | func NewFanner(jobs []Job, workers []*Kucher) *Fanner { | 
 | 	return &Fanner{ | 
 | 		jobs: jobs, workers: workers, | 
 | 		in:     make(chan Found), | 
 | 		result: make(chan int), | 
 | 	} | 
 | } | 
 |  | 
 | func (f *Fanner) Run(N int) { | 
 | 	for msg := range f.in { | 
 | 		if msg.k > f.k { | 
 | 			f.k = msg.k | 
 | 		} | 
 | 		if msg.k >= 0 { | 
 | 			f.jobsdone++ | 
 | 		} | 
 | 		if f.jobind < len(f.jobs) { | 
 | 			msg.who.in <- f.jobs[f.jobind] | 
 | 			f.jobind++ | 
 | 		} else if f.jobsdone == len(f.jobs) { | 
 | 			f.result <- f.k | 
 | 			return | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func swapped(a []int, i, j int) []int { | 
 | 	b := make([]int, len(a)) | 
 | 	copy(b, a) | 
 | 	b[i], b[j] = a[j], a[i] | 
 | 	return b | 
 | } | 
 |  | 
 | func main() { | 
 | 	flag.Parse() | 
 | 	runtime.GOMAXPROCS(*nCPU) | 
 | 	N := *n | 
 | 	base := make([]int, N) | 
 | 	for i := range base { | 
 | 		base[i] = i | 
 | 	} | 
 |  | 
 | 	njobs := 1 | 
 | 	if N > 8 { | 
 | 		njobs += (N*(N-1))/2 - 28 // njobs = 1 + sum(8..N-1) = 1 + sum(1..N-1) - sum(1..7) | 
 | 	} | 
 | 	jobs := make([]Job, njobs) | 
 | 	jobsind := 0 | 
 |  | 
 | 	firstN := N | 
 | 	if firstN > 8 { | 
 | 		firstN = 8 | 
 | 	} | 
 | 	jobs[jobsind] = Job{base, firstN} | 
 | 	jobsind++ | 
 | 	for i := N - 1; i >= 8; i-- { | 
 | 		for j := 0; j < i; j++ { | 
 | 			jobs[jobsind] = Job{swapped(base, i, j), i} | 
 | 			jobsind++ | 
 | 		} | 
 | 	} | 
 |  | 
 | 	nworkers := *nCPU | 
 | 	if njobs < nworkers { | 
 | 		nworkers = njobs | 
 | 	} | 
 | 	workers := make([]*Kucher, nworkers) | 
 | 	foreman := NewFanner(jobs, workers) | 
 | 	go foreman.Run(N) | 
 | 	for i := range workers { | 
 | 		k := NewKucher(N) | 
 | 		workers[i] = k | 
 | 		go k.Run(foreman.in) | 
 | 		foreman.in <- Found{k, -1} | 
 | 	} | 
 | 	fmt.Printf("Pfannkuchen(%d) = %d\n", N, <-foreman.result) | 
 | } |