github.com/EngineerKamesh/gofullstack@v0.0.0-20180609171605-d41341d7d4ee/volume1/section4/nilakantha/nilakantha.go (about) 1 // A concurrent computation of pi using Nilakantha's formula. 2 package main 3 4 import ( 5 "fmt" 6 "os" 7 "os/signal" 8 "time" 9 ) 10 11 // We want to create a virtual scoreboard where we can simultaneously 12 // show the current value of Pi and how many Nilakantha terms we 13 // have calculated. 14 15 // We can create the virtual scoreboard by using some ANSI escape codes 16 // Here's a wikipedia article giving you the complete list of ANSI escape 17 // codes: https://en.wikipedia.org/wiki/ANSI_escape_code 18 const ANSIClearScreenSequence = "\033[H\033[2J" 19 const ANSIFirstSlotScreenSequence = "\033[2;0H" 20 const ANSISecondSlotScreenSequence = "\033[3;0H" 21 22 // The channel used to update the current value of pi on the scoreboard 23 var pichan chan float64 = make(chan float64) 24 25 // The channel that we use to indicate that the program can exit 26 var computationDone chan bool = make(chan bool, 1) 27 28 // Number of Nilakantha terms for the scoreboard 29 var termsCount int 30 31 // This function serves as our virtual scoreboard to show the current 32 // computed value of Pi using Nilakantha's formula 33 func printCalculationSummary() { 34 35 fmt.Print(ANSIClearScreenSequence) 36 fmt.Println(ANSIFirstSlotScreenSequence, "Computed Value of Pi:\t\t", <-pichan) 37 fmt.Println(ANSISecondSlotScreenSequence, "# of Nilakantha Terms:\t\t", termsCount) 38 } 39 40 // We are going to use Nilakantha's formula from the Tantrasamgraha (which 41 // is more than 500 years old) to compute the value of Pi to several decimal 42 // points 43 func pi(n int) float64 { 44 ch := make(chan float64) 45 // The k variable is considered to be the current step 46 for k := 1; k <= n; k++ { 47 // Each Nilakantha term is calculated in its own goroutine 48 go nilakanthaTerm(ch, float64(k)) 49 } 50 f := 3.0 51 52 // We sum up the calculated Nilakantha terms for n steps 53 for k := 1; k <= n; k++ { 54 termsCount++ 55 f += <-ch 56 pichan <- f 57 } 58 59 // We notify that the computation is done over the channel 60 computationDone <- true 61 return f 62 } 63 64 // This function gives us the nilakanthaTerm for the kth step 65 func nilakanthaTerm(ch chan float64, k float64) { 66 j := 2 * k 67 if int64(k)%2 == 1 { 68 ch <- 4.0 / (j * (j + 1) * (j + 2)) 69 } else { 70 ch <- -4.0 / (j * (j + 1) * (j + 2)) 71 } 72 } 73 74 func main() { 75 76 // We use a ticker to specify the interval to update the values 77 // on the scoreboard 78 ticker := time.NewTicker(time.Millisecond * 108) 79 80 // If the user wants to prematurely break out of the program by 81 // issuing a Ctrl+C, we tell the signal package to notify us over 82 // this interrupt channel 83 interrupt := make(chan os.Signal, 1) 84 signal.Notify(interrupt, os.Interrupt) 85 86 go pi(5000) 87 88 // This anonymous function is responsible for updating the scoreboard 89 // as per the interval specified by the ticker 90 go func() { 91 for range ticker.C { 92 printCalculationSummary() 93 } 94 95 }() 96 97 for { 98 select { 99 100 // Handle the case when the computation has ended, we can 101 // end the program (exit out of this infinite loop) 102 case <-computationDone: 103 ticker.Stop() 104 fmt.Println("Program done calculating Pi.") 105 os.Exit(0) 106 107 // If the user interrupts the program (Ctrl+C) we end the 108 // program (exit out of this infinite loop) 109 case <-interrupt: 110 ticker.Stop() 111 fmt.Println("Program interrupted by the user.") 112 return 113 } 114 } 115 }