package main
import (
"context"
"fmt"
"time"
)
const (
FIZZBUZZ = iota
FIZZ
BUZZ
NOTFIZZBUZZ
)
type result byte
func (r result) String() string {
if r&(1<<FIZZBUZZ) > 0 {
return "fizzbuzz"
}
if r&(1<<FIZZ) > 0 {
return "fizz"
}
if r&(1<<BUZZ) > 0 {
return "buzz"
}
if r&(1<<NOTFIZZBUZZ) > 0 {
return "notFizzbuzz"
}
return "out-of-fizzbuzz"
}
func (r *result) Add(n int) error {
p := *r
*r = p | 1<<uint(n)
return nil
}
func recv(ctx context.Context, in <-chan int) (q int64, r result) {
defer func() { q = ctx.Value("q").(int64) }()
for {
select {
case x := <-in:
switch x {
case FIZZBUZZ:
defer r.Add(x)
return
case FIZZ:
defer r.Add(x)
case BUZZ:
defer r.Add(x)
case NOTFIZZBUZZ:
defer r.Add(x)
return
}
case <-ctx.Done():
return
}
}
return
}
func resPrint(q int64, r result) {
fmt.Printf("%v %v %v\n", time.Unix(q/1000000000, q%1000000000).Format(time.RubyDate), q, r)
}
func launch(ctx context.Context, out chan<- int) {
go do(ctx, 15, FIZZBUZZ, out)
go do(ctx, 3, FIZZ, out)
go do(ctx, 5, BUZZ, out)
go dont(ctx, NOTFIZZBUZZ, out)
}
func do(ctx context.Context, d int64, a int, out chan<- int) error {
q := ctx.Value("q").(int64)
if q != 0 && q%d == 0 {
select {
case out <- a:
case <-ctx.Done():
return ctx.Err()
}
}
return nil
}
func dont(ctx context.Context, a int, out chan<- int) error {
q := ctx.Value("q").(int64)
if q%3 != 0 && q%5 != 0 {
select {
case out <- a:
case <-ctx.Done():
return ctx.Err()
}
}
return nil
}
func main() {
for {
now := time.Now().UnixNano()
ctx := context.WithValue(context.Background(), "q", now)
go func() {
ctx, cancel := context.WithTimeout(ctx, 500*time.Millisecond)
defer cancel()
ch := make(chan int)
go launch(ctx, ch)
resPrint(recv(ctx, ch))
}()
}
}