// Copyright 2017, Joe Tsai. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.

//go:build ignore
// +build ignore

package main

import (
	"bytes"
	"go/format"
	"io/ioutil"
	"log"
	"os"
	"text/template"
)

func main() {
	if len(os.Args) != 3 {
		log.Fatalf("Usage: %s GO_TYPE OUTPUT_FILE", os.Args[0])
	}
	typ := os.Args[1]
	path := os.Args[2]

	b := new(bytes.Buffer)
	t := template.Must(template.New("source").Parse(source))
	if err := t.Execute(b, struct {
		Type, GeneratedMessage string
	}{typ, "// Code generated by sais_gen.go. DO NOT EDIT."}); err != nil {
		log.Fatalf("Template.Execute error: %v", err)
	}
	out, err := format.Source(bytes.TrimSpace(b.Bytes()))
	if err != nil {
		log.Fatalf("format.Source error: %v", err)
	}
	if err := ioutil.WriteFile(path, out, 0644); err != nil {
		log.Fatalf("ioutil.WriteFile error: %v", err)
	}
}

const source = `
// Copyright 2015, Joe Tsai. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.

{{.GeneratedMessage}}

// ====================================================
// Copyright (c) 2008-2010 Yuta Mori All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// ====================================================

package sais

func getCounts_{{.Type}}(T []{{.Type}}, C []int, n, k int) {
	var i int
	for i = 0; i < k; i++ {
		C[i] = 0
	}
	for i = 0; i < n; i++ {
		C[T[i]]++
	}
}

func getBuckets_{{.Type}}(C, B []int, k int, end bool) {
	var i, sum int
	if end {
		for i = 0; i < k; i++ {
			sum += C[i]
			B[i] = sum
		}
	} else {
		for i = 0; i < k; i++ {
			sum += C[i]
			B[i] = sum - C[i]
		}
	}
}

func sortLMS1_{{.Type}}(T []{{.Type}}, SA, C, B []int, n, k int) {
	var b, i, j int
	var c0, c1 int

	// Compute SAl.
	if &C[0] == &B[0] {
		getCounts_{{.Type}}(T, C, n, k)
	}
	getBuckets_{{.Type}}(C, B, k, false) // Find starts of buckets
	j = n - 1
	c1 = int(T[j])
	b = B[c1]
	j--
	if int(T[j]) < c1 {
		SA[b] = ^j
	} else {
		SA[b] = j
	}
	b++
	for i = 0; i < n; i++ {
		if j = SA[i]; j > 0 {
			if c0 = int(T[j]); c0 != c1 {
				B[c1] = b
				c1 = c0
				b = B[c1]
			}
			j--
			if int(T[j]) < c1 {
				SA[b] = ^j
			} else {
				SA[b] = j
			}
			b++
			SA[i] = 0
		} else if j < 0 {
			SA[i] = ^j
		}
	}

	// Compute SAs.
	if &C[0] == &B[0] {
		getCounts_{{.Type}}(T, C, n, k)
	}
	getBuckets_{{.Type}}(C, B, k, true) // Find ends of buckets
	c1 = 0
	b = B[c1]
	for i = n - 1; i >= 0; i-- {
		if j = SA[i]; j > 0 {
			if c0 = int(T[j]); c0 != c1 {
				B[c1] = b
				c1 = c0
				b = B[c1]
			}
			j--
			b--
			if int(T[j]) > c1 {
				SA[b] = ^(j + 1)
			} else {
				SA[b] = j
			}
			SA[i] = 0
		}
	}
}

func postProcLMS1_{{.Type}}(T []{{.Type}}, SA []int, n, m int) int {
	var i, j, p, q, plen, qlen, name int
	var c0, c1 int
	var diff bool

	// Compact all the sorted substrings into the first m items of SA.
	// 2*m must be not larger than n (provable).
	for i = 0; SA[i] < 0; i++ {
		SA[i] = ^SA[i]
	}
	if i < m {
		for j, i = i, i+1; ; i++ {
			if p = SA[i]; p < 0 {
				SA[j] = ^p
				j++
				SA[i] = 0
				if j == m {
					break
				}
			}
		}
	}

	// Store the length of all substrings.
	i = n - 1
	j = n - 1
	c0 = int(T[n-1])
	for {
		c1 = c0
		if i--; i < 0 {
			break
		}
		if c0 = int(T[i]); c0 < c1 {
			break
		}
	}
	for i >= 0 {
		for {
			c1 = c0
			if i--; i < 0 {
				break
			}
			if c0 = int(T[i]); c0 > c1 {
				break
			}
		}
		if i >= 0 {
			SA[m+((i+1)>>1)] = j - i
			j = i + 1
			for {
				c1 = c0
				if i--; i < 0 {
					break
				}
				if c0 = int(T[i]); c0 < c1 {
					break
				}
			}
		}
	}

	// Find the lexicographic names of all substrings.
	name = 0
	qlen = 0
	for i, q = 0, n; i < m; i++ {
		p = SA[i]
		plen = SA[m+(p>>1)]
		diff = true
		if (plen == qlen) && ((q + plen) < n) {
			for j = 0; (j < plen) && (T[p+j] == T[q+j]); j++ {
			}
			if j == plen {
				diff = false
			}
		}
		if diff {
			name++
			q = p
			qlen = plen
		}
		SA[m+(p>>1)] = name
	}
	return name
}

func sortLMS2_{{.Type}}(T []{{.Type}}, SA, C, B, D []int, n, k int) {
	var b, i, j, t, d int
	var c0, c1 int

	// Compute SAl.
	getBuckets_{{.Type}}(C, B, k, false) // Find starts of buckets
	j = n - 1
	c1 = int(T[j])
	b = B[c1]
	j--
	if int(T[j]) < c1 {
		t = 1
	} else {
		t = 0
	}
	j += n
	if t&1 > 0 {
		SA[b] = ^j
	} else {
		SA[b] = j
	}
	b++
	for i, d = 0, 0; i < n; i++ {
		if j = SA[i]; j > 0 {
			if n <= j {
				d += 1
				j -= n
			}
			if c0 = int(T[j]); c0 != c1 {
				B[c1] = b
				c1 = c0
				b = B[c1]
			}
			j--
			t = int(c0) << 1
			if int(T[j]) < c1 {
				t |= 1
			}
			if D[t] != d {
				j += n
				D[t] = d
			}
			if t&1 > 0 {
				SA[b] = ^j
			} else {
				SA[b] = j
			}
			b++
			SA[i] = 0
		} else if j < 0 {
			SA[i] = ^j
		}
	}
	for i = n - 1; 0 <= i; i-- {
		if SA[i] > 0 {
			if SA[i] < n {
				SA[i] += n
				for j = i - 1; SA[j] < n; j-- {
				}
				SA[j] -= n
				i = j
			}
		}
	}

	// Compute SAs.
	getBuckets_{{.Type}}(C, B, k, true) // Find ends of buckets
	c1 = 0
	b = B[c1]
	for i, d = n-1, d+1; i >= 0; i-- {
		if j = SA[i]; j > 0 {
			if n <= j {
				d += 1
				j -= n
			}
			if c0 = int(T[j]); c0 != c1 {
				B[c1] = b
				c1 = c0
				b = B[c1]
			}
			j--
			t = int(c0) << 1
			if int(T[j]) > c1 {
				t |= 1
			}
			if D[t] != d {
				j += n
				D[t] = d
			}
			b--
			if t&1 > 0 {
				SA[b] = ^(j + 1)
			} else {
				SA[b] = j
			}
			SA[i] = 0
		}
	}
}

func postProcLMS2_{{.Type}}(SA []int, n, m int) int {
	var i, j, d, name int

	// Compact all the sorted LMS substrings into the first m items of SA.
	name = 0
	for i = 0; SA[i] < 0; i++ {
		j = ^SA[i]
		if n <= j {
			name += 1
		}
		SA[i] = j
	}
	if i < m {
		for d, i = i, i+1; ; i++ {
			if j = SA[i]; j < 0 {
				j = ^j
				if n <= j {
					name += 1
				}
				SA[d] = j
				d++
				SA[i] = 0
				if d == m {
					break
				}
			}
		}
	}
	if name < m {
		// Store the lexicographic names.
		for i, d = m-1, name+1; 0 <= i; i-- {
			if j = SA[i]; n <= j {
				j -= n
				d--
			}
			SA[m+(j>>1)] = d
		}
	} else {
		// Unset flags.
		for i = 0; i < m; i++ {
			if j = SA[i]; n <= j {
				j -= n
				SA[i] = j
			}
		}
	}
	return name
}

func induceSA_{{.Type}}(T []{{.Type}}, SA, C, B []int, n, k int) {
	var b, i, j int
	var c0, c1 int

	// Compute SAl.
	if &C[0] == &B[0] {
		getCounts_{{.Type}}(T, C, n, k)
	}
	getBuckets_{{.Type}}(C, B, k, false) // Find starts of buckets
	j = n - 1
	c1 = int(T[j])
	b = B[c1]
	if j > 0 && int(T[j-1]) < c1 {
		SA[b] = ^j
	} else {
		SA[b] = j
	}
	b++
	for i = 0; i < n; i++ {
		j = SA[i]
		SA[i] = ^j
		if j > 0 {
			j--
			if c0 = int(T[j]); c0 != c1 {
				B[c1] = b
				c1 = c0
				b = B[c1]
			}
			if j > 0 && int(T[j-1]) < c1 {
				SA[b] = ^j
			} else {
				SA[b] = j
			}
			b++
		}
	}

	// Compute SAs.
	if &C[0] == &B[0] {
		getCounts_{{.Type}}(T, C, n, k)
	}
	getBuckets_{{.Type}}(C, B, k, true) // Find ends of buckets
	c1 = 0
	b = B[c1]
	for i = n - 1; i >= 0; i-- {
		if j = SA[i]; j > 0 {
			j--
			if c0 = int(T[j]); c0 != c1 {
				B[c1] = b
				c1 = c0
				b = B[c1]
			}
			b--
			if (j == 0) || (int(T[j-1]) > c1) {
				SA[b] = ^j
			} else {
				SA[b] = j
			}
		} else {
			SA[i] = ^j
		}
	}
}

func computeSA_{{.Type}}(T []{{.Type}}, SA []int, fs, n, k int) {
	const (
		minBucketSize = 512
		sortLMS2Limit = 0x3fffffff
	)

	var C, B, D, RA []int
	var bo int // Offset of B relative to SA
	var b, i, j, m, p, q, name, newfs int
	var c0, c1 int
	var flags uint

	if k <= minBucketSize {
		C = make([]int, k)
		if k <= fs {
			bo = n + fs - k
			B = SA[bo:]
			flags = 1
		} else {
			B = make([]int, k)
			flags = 3
		}
	} else if k <= fs {
		C = SA[n+fs-k:]
		if k <= fs-k {
			bo = n + fs - 2*k
			B = SA[bo:]
			flags = 0
		} else if k <= 4*minBucketSize {
			B = make([]int, k)
			flags = 2
		} else {
			B = C
			flags = 8
		}
	} else {
		C = make([]int, k)
		B = C
		flags = 4 | 8
	}
	if n <= sortLMS2Limit && 2 <= (n/k) {
		if flags&1 > 0 {
			if 2*k <= fs-k {
				flags |= 32
			} else {
				flags |= 16
			}
		} else if flags == 0 && 2*k <= (fs-2*k) {
			flags |= 32
		}
	}

	// Stage 1: Reduce the problem by at least 1/2.
	// Sort all the LMS-substrings.
	getCounts_{{.Type}}(T, C, n, k)
	getBuckets_{{.Type}}(C, B, k, true) // Find ends of buckets
	for i = 0; i < n; i++ {
		SA[i] = 0
	}
	b = -1
	i = n - 1
	j = n
	m = 0
	c0 = int(T[n-1])
	for {
		c1 = c0
		if i--; i < 0 {
			break
		}
		if c0 = int(T[i]); c0 < c1 {
			break
		}
	}
	for i >= 0 {
		for {
			c1 = c0
			if i--; i < 0 {
				break
			}
			if c0 = int(T[i]); c0 > c1 {
				break
			}
		}
		if i >= 0 {
			if b >= 0 {
				SA[b] = j
			}
			B[c1]--
			b = B[c1]
			j = i
			m++
			for {
				c1 = c0
				if i--; i < 0 {
					break
				}
				if c0 = int(T[i]); c0 < c1 {
					break
				}
			}
		}
	}

	if m > 1 {
		if flags&(16|32) > 0 {
			if flags&16 > 0 {
				D = make([]int, 2*k)
			} else {
				D = SA[bo-2*k:]
			}
			B[T[j+1]]++
			for i, j = 0, 0; i < k; i++ {
				j += C[i]
				if B[i] != j {
					SA[B[i]] += n
				}
				D[i] = 0
				D[i+k] = 0
			}
			sortLMS2_{{.Type}}(T, SA, C, B, D, n, k)
			name = postProcLMS2_{{.Type}}(SA, n, m)
		} else {
			sortLMS1_{{.Type}}(T, SA, C, B, n, k)
			name = postProcLMS1_{{.Type}}(T, SA, n, m)
		}
	} else if m == 1 {
		SA[b] = j + 1
		name = 1
	} else {
		name = 0
	}

	// Stage 2: Solve the reduced problem.
	// Recurse if names are not yet unique.
	if name < m {
		newfs = n + fs - 2*m
		if flags&(1|4|8) == 0 {
			if k+name <= newfs {
				newfs -= k
			} else {
				flags |= 8
			}
		}
		RA = SA[m+newfs:]
		for i, j = m+(n>>1)-1, m-1; m <= i; i-- {
			if SA[i] != 0 {
				RA[j] = SA[i] - 1
				j--
			}
		}
		computeSA_int(RA, SA, newfs, m, name)

		i = n - 1
		j = m - 1
		c0 = int(T[n-1])
		for {
			c1 = c0
			if i--; i < 0 {
				break
			}
			if c0 = int(T[i]); c0 < c1 {
				break
			}
		}
		for i >= 0 {
			for {
				c1 = c0
				if i--; i < 0 {
					break
				}
				if c0 = int(T[i]); c0 > c1 {
					break
				}
			}
			if i >= 0 {
				RA[j] = i + 1
				j--
				for {
					c1 = c0
					if i--; i < 0 {
						break
					}
					if c0 = int(T[i]); c0 < c1 {
						break
					}
				}
			}
		}
		for i = 0; i < m; i++ {
			SA[i] = RA[SA[i]]
		}
		if flags&4 > 0 {
			B = make([]int, k)
			C = B
		}
		if flags&2 > 0 {
			B = make([]int, k)
		}
	}

	// Stage 3: Induce the result for the original problem.
	if flags&8 > 0 {
		getCounts_{{.Type}}(T, C, n, k)
	}
	// Put all left-most S characters into their buckets.
	if m > 1 {
		getBuckets_{{.Type}}(C, B, k, true) // Find ends of buckets
		i = m - 1
		j = n
		p = SA[m-1]
		c1 = int(T[p])
		for {
			c0 = c1
			q = B[c0]
			for q < j {
				j--
				SA[j] = 0
			}
			for {
				j--
				SA[j] = p
				if i--; i < 0 {
					break
				}
				p = SA[i]
				if c1 = int(T[p]); c1 != c0 {
					break
				}
			}
			if i < 0 {
				break
			}
		}
		for j > 0 {
			j--
			SA[j] = 0
		}
	}
	induceSA_{{.Type}}(T, SA, C, B, n, k)
}
`
