| /* |
| 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", 2, "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) |
| } |