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  }