/ golang

Input template (Go) for algorithmic competitions

Since I have been playing with Go recently, I decided to use it for the qualification round of Google Code Jam - 2016.

Here is the struct I wrote to wrap around a reader so as to handle input from either a file or os.STDIN (technicall any io.Reader).

type MyInput struct {
	rdr         io.Reader
	lineChan    chan string
	initialized bool
}

func (mi *MyInput) start(done chan struct{}) {
	r := bufio.NewReader(mi.rdr)
	defer func() { close(mi.lineChan) }()
	for {
		line, err := r.ReadString('\n')
		if !mi.initialized {
			mi.initialized = true
			done <- struct{}{}
		}
		mi.lineChan <- strings.TrimSpace(line)
		if err == io.EOF {
			break
		}
		if err != nil {
			panic(err)
		}
	}
}

Its just a simple wrapper around a reader with some state variables.
Next, I added the function readLine() which will be the base of all functions for reading from the input (file or stdin or whatever).

func (mi *MyInput) readLine() string {
	// if this is the first call, initialize
	if !mi.initialized {
		mi.lineChan = make(chan string)
		done := make(chan struct{})
		go mi.start(done)
		<-done
	}

	res, ok := <-mi.lineChan
	if !ok {
		panic("trying to read from a closed channel")
	}
	return res
}

The first call to readLine() would initialize the MyInput instance, in addition to returning a line from the input.

Next I added some helper functions to cater to the most common use-cases for reading from an input, as far as algorithmic completions input is concerned.

// to read an int from a line that contains only one value and that is an int
func (mi *MyInput) readInt() int {
	line := mi.readLine()
	i, err := strconv.Atoi(line)
	if err != nil {
		panic(err)
	}
	return i
}

// similar to `readInt` but returns an int64
func (mi *MyInput) readInt64() int64 {
	line := mi.readLine()
	i, err := strconv.ParseInt(line, 10, 64)
	if err != nil {
		panic(err)
	}
	return i
}

// if a line has multiple ints seperated by a space, 
//use this to get the ints in a slice []int
// e.g. if line is "1 2 6 77"
// this would return []int{1,2,6,77}
func (mi *MyInput) readInts() []int {
	line := mi.readLine()
	parts := strings.Split(line, " ")
	res := []int{}
	for _, s := range parts {
		tmp, err := strconv.Atoi(s)
		if err != nil {
			panic(err)
		}
		res = append(res, tmp)
	}
	return res
}

// similar to `readInts` but returns a slice of int64s
func (mi *MyInput) readInt64s() []int64 {
	line := mi.readLine()
	parts := strings.Split(line, " ")
	res := []int64{}
	for _, s := range parts {
		tmp, err := strconv.ParseInt(s, 10, 64)
		if err != nil {
			panic(err)
		}
		res = append(res, tmp)
	}
	return res
}

// returns the words from the line that are seperated by a space
// e.g. if line is "who art thou",
// this would return []string{"who", "art", "thou"}
func (mi *MyInput) readWords() []string {
	line := mi.readLine()
	return strings.Split(line, " ")
}

Using MyInput is as simple as shown below:

func main() {
	f, _ := os.Open("input_file.in")
	mi := MyInput{rdr: f}
	// mi := MyInput{rdr: os.Stdin}

	t := mi.readInt()
	for caseNo := 1; caseNo <= t; caseNo++ {
		// TODO - solve the case !
	}
}

The complete code is available at as a Gitlab snippet here