<commit_before>package v3

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"path"
	"strings"
	"time"

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry-incubator/cf-test-helpers/generator"
	"github.com/cloudfoundry-incubator/cf-test-helpers/runner"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/app_helpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/assets"
	. "github.com/cloudfoundry/cf-acceptance-tests/helpers/v3_helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
	archive_helpers "github.com/pivotal-golang/archiver/extractor/test_helper"
)

var _ = Describe("buildpack", func() {
	var (
		appName       string
		appGuid       string
		buildpackName string
		buildpackGuid string
		packageGuid   string
		spaceGuid     string
		token         string
	)

	BeforeEach(func() {
		appName = generator.PrefixedRandomName("CATS-APP-")
		spaceGuid = GetSpaceGuidFromName(context.RegularUserContext().Space)
		appGuid = CreateApp(appName, spaceGuid, "{}")
		packageGuid = CreatePackage(appGuid)
		token = GetAuthToken()
		uploadUrl := fmt.Sprintf("%s/v3/packages/%s/upload", config.ApiEndpoint, packageGuid)
		UploadPackage(uploadUrl, assets.NewAssets().DoraZip, token)
		WaitForPackageToBeReady(packageGuid)

		buildpackName = generator.PrefixedRandomName("CATS-BP-")
		buildpackZip := createBuildpack()

		cf.AsUser(context.AdminUserContext(), DEFAULT_TIMEOUT, func() {
			Expect(cf.Cf("create-buildpack", buildpackName, buildpackZip, "999").Wait(DEFAULT_TIMEOUT)).To(Exit(0))
		})

		session := cf.Cf("curl", fmt.Sprintf("/v2/buildpacks?q=name:%s", buildpackName))
		bytes := session.Wait(DEFAULT_TIMEOUT).Out.Contents()
		var buildpack struct {
			Resources []struct {
				Metadata struct {
					Guid string `json:"guid"`
				} `json:"metadata"`
			} `json:"resources"`
		}
		json.Unmarshal(bytes, &buildpack)
		buildpackGuid = buildpack.Resources[0].Metadata.Guid
	})

	AfterEach(func() {
		app_helpers.AppReport(appName, DEFAULT_TIMEOUT)

		cf.AsUser(context.AdminUserContext(), DEFAULT_TIMEOUT, func() {
			Expect(cf.Cf("delete-buildpack", buildpackName, "-f").Wait(DEFAULT_TIMEOUT)).To(Exit(0))
		})
		DeleteApp(appGuid)
	})

	It("Stages with a user specified admin buildpack", func() {
		StagePackage(packageGuid, fmt.Sprintf(`{"lifecycle":{ "type": "buildpack", "data": { "buildpack": "%s" } }}`, buildpackName))

		logUrl := fmt.Sprintf("loggregator.%s/recent?app=%s", config.AppsDomain, appGuid)
		Eventually(func() *Session {
			session := runner.Curl(logUrl, "-H", fmt.Sprintf("Authorization: %s", token))
			Expect(session.Wait(DEFAULT_TIMEOUT)).To(Exit(0))
			return session
		}, 1*time.Minute, 10*time.Second).Should(Say("STAGED WITH CUSTOM BUILDPACK"))
	})

	It("Stages with a user specified github buildpack", func() {
		StagePackage(packageGuid, `{"lifecycle":{ "type": "buildpack", "data": { "buildpack": "http://github.com/cloudfoundry/go-buildpack" } }`)

		logUrl := fmt.Sprintf("loggregator.%s/recent?app=%s", config.AppsDomain, appGuid)
		Eventually(func() *Session {
			session := runner.Curl(logUrl, "-H", fmt.Sprintf("Authorization: %s", token))
			Expect(session.Wait(DEFAULT_TIMEOUT)).To(Exit(0))
			return session
		}, 3*time.Minute, 10*time.Second).Should(Say("Godeps"))
	})

	It("uses buildpack cache for staging", func() {
		firstDropletGuid := StagePackage(packageGuid, fmt.Sprintf(`{"lifecycle":{ "type": "buildpack", "data": { "buildpack": "%s" } }}`, buildpackName))
		dropletPath := fmt.Sprintf("/v3/droplets/%s", firstDropletGuid)
		Eventually(func() *Session {
			result := cf.Cf("curl", dropletPath).Wait(DEFAULT_TIMEOUT)
			if strings.Contains(string(result.Out.Contents()), "FAILED") {
				Fail("staging failed")
			}
			return result
		}, CF_PUSH_TIMEOUT).Should(Say("custom buildpack contents - cache not found"))

		// Wait for buildpack cache to be uploaded to blobstore.
		time.Sleep(DEFAULT_TIMEOUT)

		secondDropletGuid := StagePackage(packageGuid, fmt.Sprintf(`{"lifecycle":{ "type": "buildpack", "data": { "buildpack": "%s" } }}`, buildpackName))
		dropletPath = fmt.Sprintf("/v3/droplets/%s", secondDropletGuid)
		Eventually(func() *Session {
			result := cf.Cf("curl", dropletPath).Wait(DEFAULT_TIMEOUT)
			if strings.Contains(string(result.Out.Contents()), "FAILED") {
				Fail("staging failed")
			}
			if strings.Contains(string(result.Out.Contents()), "cache not found") {
				Fail("cache was not found")
			}
			return result
		}, CF_PUSH_TIMEOUT).Should(Say("custom buildpack contents - here's a cache"))

		Expect(secondDropletGuid).NotTo(Equal(firstDropletGuid))
	})
})

func createBuildpack() string {
	tmpPath, err := ioutil.TempDir("", "buildpack-cats")
	Expect(err).ToNot(HaveOccurred())

	buildpackArchivePath := path.Join(tmpPath, "buildpack.zip")

	archive_helpers.CreateZipArchive(buildpackArchivePath, []archive_helpers.ArchiveFile{
		{
			Name: "bin/compile",
			Body: `#!/usr/bin/env bash

echo "STAGED WITH CUSTOM BUILDPACK"

mkdir -p $1 $2
if [ -f "$2/cached-file" ]; then
cp $2/cached-file $1/content
else
echo "cache not found" > $1/content
fi

content=$(cat $1/content)
echo "web: while true; do { echo -e 'HTTP/1.1 200 OK\r\n'; echo "custom buildpack contents - $content"; } | nc -l \$PORT; done" > $1/Procfile

echo "here's a cache" > $2/cached-file
`,
		},
		{
			Name: "bin/detect",
			Body: `#!/bin/bash
echo no
exit 1
`,
		},
		{
			Name: "bin/release",
			Body: `#!/usr/bin/env bash

cat <<EOF
---
config_vars:
  PATH: bin:/usr/local/bin:/usr/bin:/bin
  FROM_BUILD_PACK: "yes"
default_process_types:
  web: while true; do { echo -e 'HTTP/1.1 200 OK\r\n'; echo "custom buildpack contents - $content"; } | nc -l \$PORT; done
EOF
`,
		},
	})

	return buildpackArchivePath
}
<commit_msg>Remove unneeded buildpackGuid lookup in buildpacks_test<commit_after>package v3

import (
	"fmt"
	"io/ioutil"
	"path"
	"strings"
	"time"

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry-incubator/cf-test-helpers/generator"
	"github.com/cloudfoundry-incubator/cf-test-helpers/runner"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/app_helpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/assets"
	. "github.com/cloudfoundry/cf-acceptance-tests/helpers/v3_helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
	archive_helpers "github.com/pivotal-golang/archiver/extractor/test_helper"
)

var _ = Describe("buildpack", func() {
	var (
		appName       string
		appGuid       string
		buildpackName string
		packageGuid   string
		spaceGuid     string
		token         string
	)

	BeforeEach(func() {
		appName = generator.PrefixedRandomName("CATS-APP-")
		spaceGuid = GetSpaceGuidFromName(context.RegularUserContext().Space)
		appGuid = CreateApp(appName, spaceGuid, "{}")
		packageGuid = CreatePackage(appGuid)
		token = GetAuthToken()
		uploadUrl := fmt.Sprintf("%s/v3/packages/%s/upload", config.ApiEndpoint, packageGuid)
		UploadPackage(uploadUrl, assets.NewAssets().DoraZip, token)
		WaitForPackageToBeReady(packageGuid)

		buildpackName = generator.PrefixedRandomName("CATS-BP-")
		buildpackZip := createBuildpack()

		cf.AsUser(context.AdminUserContext(), DEFAULT_TIMEOUT, func() {
			Expect(cf.Cf("create-buildpack", buildpackName, buildpackZip, "999").Wait(DEFAULT_TIMEOUT)).To(Exit(0))
		})
	})

	AfterEach(func() {
		app_helpers.AppReport(appName, DEFAULT_TIMEOUT)

		cf.AsUser(context.AdminUserContext(), DEFAULT_TIMEOUT, func() {
			Expect(cf.Cf("delete-buildpack", buildpackName, "-f").Wait(DEFAULT_TIMEOUT)).To(Exit(0))
		})
		DeleteApp(appGuid)
	})

	It("Stages with a user specified admin buildpack", func() {
		StagePackage(packageGuid, fmt.Sprintf(`{"lifecycle":{ "type": "buildpack", "data": { "buildpack": "%s" } }}`, buildpackName))

		logUrl := fmt.Sprintf("loggregator.%s/recent?app=%s", config.AppsDomain, appGuid)
		Eventually(func() *Session {
			session := runner.Curl(logUrl, "-H", fmt.Sprintf("Authorization: %s", token))
			Expect(session.Wait(DEFAULT_TIMEOUT)).To(Exit(0))
			return session
		}, 1*time.Minute, 10*time.Second).Should(Say("STAGED WITH CUSTOM BUILDPACK"))
	})

	It("Stages with a user specified github buildpack", func() {
		StagePackage(packageGuid, `{"lifecycle":{ "type": "buildpack", "data": { "buildpack": "http://github.com/cloudfoundry/go-buildpack" } }`)

		logUrl := fmt.Sprintf("loggregator.%s/recent?app=%s", config.AppsDomain, appGuid)
		Eventually(func() *Session {
			session := runner.Curl(logUrl, "-H", fmt.Sprintf("Authorization: %s", token))
			Expect(session.Wait(DEFAULT_TIMEOUT)).To(Exit(0))
			return session
		}, 3*time.Minute, 10*time.Second).Should(Say("Godeps"))
	})

	It("uses buildpack cache for staging", func() {
		firstDropletGuid := StagePackage(packageGuid, fmt.Sprintf(`{"lifecycle":{ "type": "buildpack", "data": { "buildpack": "%s" } }}`, buildpackName))
		dropletPath := fmt.Sprintf("/v3/droplets/%s", firstDropletGuid)
		Eventually(func() *Session {
			result := cf.Cf("curl", dropletPath).Wait(DEFAULT_TIMEOUT)
			if strings.Contains(string(result.Out.Contents()), "FAILED") {
				Fail("staging failed")
			}
			return result
		}, CF_PUSH_TIMEOUT).Should(Say("custom buildpack contents - cache not found"))

		// Wait for buildpack cache to be uploaded to blobstore.
		time.Sleep(DEFAULT_TIMEOUT)

		secondDropletGuid := StagePackage(packageGuid, fmt.Sprintf(`{"lifecycle":{ "type": "buildpack", "data": { "buildpack": "%s" } }}`, buildpackName))
		dropletPath = fmt.Sprintf("/v3/droplets/%s", secondDropletGuid)
		Eventually(func() *Session {
			result := cf.Cf("curl", dropletPath).Wait(DEFAULT_TIMEOUT)
			if strings.Contains(string(result.Out.Contents()), "FAILED") {
				Fail("staging failed")
			}
			if strings.Contains(string(result.Out.Contents()), "cache not found") {
				Fail("cache was not found")
			}
			return result
		}, CF_PUSH_TIMEOUT).Should(Say("custom buildpack contents - here's a cache"))

		Expect(secondDropletGuid).NotTo(Equal(firstDropletGuid))
	})
})

func createBuildpack() string {
	tmpPath, err := ioutil.TempDir("", "buildpack-cats")
	Expect(err).ToNot(HaveOccurred())

	buildpackArchivePath := path.Join(tmpPath, "buildpack.zip")

	archive_helpers.CreateZipArchive(buildpackArchivePath, []archive_helpers.ArchiveFile{
		{
			Name: "bin/compile",
			Body: `#!/usr/bin/env bash

echo "STAGED WITH CUSTOM BUILDPACK"

mkdir -p $1 $2
if [ -f "$2/cached-file" ]; then
cp $2/cached-file $1/content
else
echo "cache not found" > $1/content
fi

content=$(cat $1/content)
echo "web: while true; do { echo -e 'HTTP/1.1 200 OK\r\n'; echo "custom buildpack contents - $content"; } | nc -l \$PORT; done" > $1/Procfile

echo "here's a cache" > $2/cached-file
`,
		},
		{
			Name: "bin/detect",
			Body: `#!/bin/bash
echo no
exit 1
`,
		},
		{
			Name: "bin/release",
			Body: `#!/usr/bin/env bash

cat <<EOF
---
config_vars:
  PATH: bin:/usr/local/bin:/usr/bin:/bin
  FROM_BUILD_PACK: "yes"
default_process_types:
  web: while true; do { echo -e 'HTTP/1.1 200 OK\r\n'; echo "custom buildpack contents - $content"; } | nc -l \$PORT; done
EOF
`,
		},
	})

	return buildpackArchivePath
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strconv"
	"sync"
	"time"
)

var (
	debug                  bool
	verbose                bool
	info                   bool
	force                  bool
	usemove                bool
	pfMode                 bool
	dryRun                 bool
	check4update           bool
	checkSum               bool
	config                 ConfigSettings
	wg                     sync.WaitGroup
	mutex                  sync.Mutex
	empty                  struct{}
	syncGitCount           int
	syncForgeCount         int
	needSyncGitCount       int
	needSyncForgeCount     int
	syncGitTime            float64
	syncForgeTime          float64
	ioGitTime              float64
	ioForgeTime            float64
	forgeJsonParseTime     float64
	metadataJsonParseTime  float64
	gmetadataJsonParseTime float64
	buildtime              string
	uniqueForgeModules     map[string]ForgeModule
	latestForgeModules     LatestForgeModules
)

type LatestForgeModules struct {
	sync.RWMutex
	m map[string]string
}

// ConfigSettings contains the key value pairs from the g10k config file
type ConfigSettings struct {
	CacheDir        string `yaml:"cachedir"`
	ForgeCacheDir   string
	ModulesCacheDir string
	EnvCacheDir     string
	Git             Git
	Forge           Forge
	Sources         map[string]Source
	Timeout         int `yaml:"timeout"`
}

type Forge struct {
	Baseurl string `yaml:"baseurl"`
}

type Git struct {
	privateKey string `yaml:"private_key"`
	username   string
}

// Source contains basic information about a Puppet environment repository
type Source struct {
	Remote             string
	Basedir            string
	Prefix             string
	PrivateKey         string `yaml:"private_key"`
	ForceForgeVersions bool   `yaml:"force_forge_versions"`
}

// Puppetfile contains the key value pairs from the Puppetfile
type Puppetfile struct {
	moduleDir     string
	forgeBaseURL  string
	forgeCacheTtl time.Duration
	forgeModules  map[string]ForgeModule
	gitModules    map[string]GitModule
	privateKey    string
	source        string
	workDir       string
}

// ForgeModule contains information (Version, Name, Author, md5 checksum, file size of the tar.gz archive, Forge BaseURL if custom) about a Puppetlabs Forge module
type ForgeModule struct {
	version   string
	name      string
	author    string
	md5sum    string
	fileSize  int64
	baseUrl   string
	cacheTtl  time.Duration
	sha256sum string
}

// GitModule contains information about a Git Puppet module
type GitModule struct {
	privateKey        string
	git               string
	branch            string
	tag               string
	commit            string
	ref               string
	link              bool
	ignoreUnreachable bool
	fallback          []string
}

// ForgeResult is returned by queryForgeAPI and contains if and which version of the Puppetlabs Forge module needs to be downloaded
type ForgeResult struct {
	needToGet     bool
	versionNumber string
	md5sum        string
	fileSize      int64
}

// ExecResult contains the exit code and output of an external command (e.g. git)
type ExecResult struct {
	returnCode int
	output     string
}

func main() {

	var (
		configFile       = flag.String("config", "", "which config file to use")
		envBranchFlag    = flag.String("branch", "", "which git branch of the Puppet environment to update, e.g. core_foobar")
		pfFlag           = flag.Bool("puppetfile", false, "install all modules from Puppetfile in cwd")
		forceFlag        = flag.Bool("force", false, "purge the Puppet environment directory and do a full sync")
		dryRunFlag       = flag.Bool("dryrun", false, "do not modify anything, just print what would be changed")
		usemoveFlag      = flag.Bool("usemove", false, "do not use hardlinks to populate your Puppet environments with Puppetlabs Forge modules. Instead uses simple move commands and purges the Forge cache directory after each run! (Useful for g10k runs inside a Docker container)")
		check4updateFlag = flag.Bool("check4update", false, "only check if the is newer version of the Puppet module avaialable. Does implicitly set dryrun to true")
		checkSumFlag     = flag.Bool("checksum", false, "get the md5 check sum for each Puppetlabs Forge module and verify the integrity of the downloaded archive. Increases g10k run time!")
		debugFlag        = flag.Bool("debug", false, "log debug output, defaults to false")
		verboseFlag      = flag.Bool("verbose", false, "log verbose output, defaults to false")
		infoFlag         = flag.Bool("info", false, "log info output, defaults to false")
		versionFlag      = flag.Bool("version", false, "show build time and version number")
	)
	flag.Parse()

	debug = *debugFlag
	verbose = *verboseFlag
	info = *infoFlag
	force = *forceFlag
	dryRun = *dryRunFlag
	check4update = *check4updateFlag
	usemove = *usemoveFlag
	pfMode = *pfFlag
	checkSum = *checkSumFlag

	if *versionFlag {
		fmt.Println("g10k Version 1.0 Build time:", buildtime, "UTC")
		os.Exit(0)
	}

	if check4update {
		dryRun = true
	}

	target := ""
	before := time.Now()
	if len(*configFile) > 0 {
		if usemove {
			Fatalf("Error: -usemove parameter is only allowed in -puppetfile mode!")
		}
		if pfMode {
			Fatalf("Error: -puppetfile parameter is not allowed with -config parameter!")
		}
		Debugf("Using as config file: " + *configFile)
		config = readConfigfile(*configFile)
		target = *configFile
		if len(*envBranchFlag) > 0 {
			resolvePuppetEnvironment(*envBranchFlag)
			target += " with branch " + *envBranchFlag
		} else {
			resolvePuppetEnvironment("")
		}
	} else {
		if pfMode {
			Debugf("Trying to use as Puppetfile: ./Puppetfile")
			sm := make(map[string]Source)
			sm["cmdlineparam"] = Source{Basedir: "."}
			cachedir := "/tmp/g10k"
			if len(os.Getenv("g10k_cachedir")) > 0 {
				cachedir = os.Getenv("g10k_cachedir")
				cachedir = checkDirAndCreate(cachedir, "cachedir environment variable g10k_cachedir")
				Debugf("Found environment variable g10k_cachedir set to: " + cachedir)
			} else {
				cachedir = checkDirAndCreate(cachedir, "cachedir default value")
			}
			//config = ConfigSettings{CacheDir: cachedir, ForgeCacheDir: cachedir, ModulesCacheDir: cachedir, EnvCacheDir: cachedir, Forge:{Baseurl: "https://forgeapi.puppetlabs.com"}, Sources: sm}
			forgeDefaultSettings := Forge{Baseurl: "https://forgeapi.puppetlabs.com"}
			config = ConfigSettings{CacheDir: cachedir, ForgeCacheDir: cachedir, ModulesCacheDir: cachedir, EnvCacheDir: cachedir, Sources: sm, Forge: forgeDefaultSettings}
			target = "./Puppetfile"
			puppetfile := readPuppetfile("./Puppetfile", "", "cmdlineparam", false)
			puppetfile.workDir = "."
			pfm := make(map[string]Puppetfile)
			pfm["cmdlineparam"] = puppetfile
			resolvePuppetfile(pfm)
		} else {
			Fatalf("Error: you need to specify at least a config file or use the Puppetfile mode\nExample call: " + os.Args[0] + " -config test.yaml or " + os.Args[0] + " -puppetfile\n")
		}
	}

	if usemove {
		// we can not reuse the Forge cache at all when -usemove gets used, because we can not delete the -latest link for some reason
		defer purgeDir(config.ForgeCacheDir, "main() -puppetfile mode with -usemove parameter")
	}

	// DEBUG
	//pf := make(map[string]Puppetfile)
	//pf["core_fullmanaged"] = readPuppetfile("/tmp/core/core_fullmanaged/", "/home/andpaul/dev/go/src/github.com/xorpaul/g10k/portal_envs")
	//pf["itodsi_corosync"] = readPuppetfile("/tmp/itodsi/itodsi_corosync/", "/home/andpaul/dev/go/src/github.com/xorpaul/g10k/portal_envs")
	//resolvePuppetfile(pf)
	//resolveGitRepositories(config)
	//resolveForgeModules(configSettings.forge)
	//doModuleInstallOrNothing("camptocamp-postfix-1.2.2", "/tmp/g10k/camptocamp-postfix-1.2.2")
	//doModuleInstallOrNothing("saz-resolv_conf-latest")
	//readModuleMetadata("/tmp/g10k/forge/camptocamp-postfix-1.2.2/metadata.json")

	Debugf("Forge response JSON parsing took " + strconv.FormatFloat(forgeJsonParseTime, 'f', 4, 64) + " seconds")
	Debugf("Forge modules metadata.json parsing took " + strconv.FormatFloat(metadataJsonParseTime, 'f', 4, 64) + " seconds")

	if !check4update {
		fmt.Println("Synced", target, "with", syncGitCount, "git repositories and", syncForgeCount, "Forge modules in "+strconv.FormatFloat(time.Since(before).Seconds(), 'f', 1, 64)+"s with git ("+strconv.FormatFloat(syncGitTime, 'f', 1, 64)+"s sync, I/O", strconv.FormatFloat(ioGitTime, 'f', 1, 64)+"s) and Forge ("+strconv.FormatFloat(syncForgeTime, 'f', 1, 64)+"s query+download, I/O", strconv.FormatFloat(ioForgeTime, 'f', 1, 64)+"s)")
	}
	if dryRun && (needSyncForgeCount > 0 || needSyncGitCount > 0) {
		os.Exit(1)
	}
}
<commit_msg>look for git executable first<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"sync"
	"time"
)

var (
	debug                  bool
	verbose                bool
	info                   bool
	force                  bool
	usemove                bool
	pfMode                 bool
	dryRun                 bool
	check4update           bool
	checkSum               bool
	config                 ConfigSettings
	wg                     sync.WaitGroup
	mutex                  sync.Mutex
	empty                  struct{}
	syncGitCount           int
	syncForgeCount         int
	needSyncGitCount       int
	needSyncForgeCount     int
	syncGitTime            float64
	syncForgeTime          float64
	ioGitTime              float64
	ioForgeTime            float64
	forgeJsonParseTime     float64
	metadataJsonParseTime  float64
	gmetadataJsonParseTime float64
	buildtime              string
	uniqueForgeModules     map[string]ForgeModule
	latestForgeModules     LatestForgeModules
)

type LatestForgeModules struct {
	sync.RWMutex
	m map[string]string
}

// ConfigSettings contains the key value pairs from the g10k config file
type ConfigSettings struct {
	CacheDir        string `yaml:"cachedir"`
	ForgeCacheDir   string
	ModulesCacheDir string
	EnvCacheDir     string
	Git             Git
	Forge           Forge
	Sources         map[string]Source
	Timeout         int `yaml:"timeout"`
}

type Forge struct {
	Baseurl string `yaml:"baseurl"`
}

type Git struct {
	privateKey string `yaml:"private_key"`
	username   string
}

// Source contains basic information about a Puppet environment repository
type Source struct {
	Remote             string
	Basedir            string
	Prefix             string
	PrivateKey         string `yaml:"private_key"`
	ForceForgeVersions bool   `yaml:"force_forge_versions"`
}

// Puppetfile contains the key value pairs from the Puppetfile
type Puppetfile struct {
	moduleDir     string
	forgeBaseURL  string
	forgeCacheTtl time.Duration
	forgeModules  map[string]ForgeModule
	gitModules    map[string]GitModule
	privateKey    string
	source        string
	workDir       string
}

// ForgeModule contains information (Version, Name, Author, md5 checksum, file size of the tar.gz archive, Forge BaseURL if custom) about a Puppetlabs Forge module
type ForgeModule struct {
	version   string
	name      string
	author    string
	md5sum    string
	fileSize  int64
	baseUrl   string
	cacheTtl  time.Duration
	sha256sum string
}

// GitModule contains information about a Git Puppet module
type GitModule struct {
	privateKey        string
	git               string
	branch            string
	tag               string
	commit            string
	ref               string
	link              bool
	ignoreUnreachable bool
	fallback          []string
}

// ForgeResult is returned by queryForgeAPI and contains if and which version of the Puppetlabs Forge module needs to be downloaded
type ForgeResult struct {
	needToGet     bool
	versionNumber string
	md5sum        string
	fileSize      int64
}

// ExecResult contains the exit code and output of an external command (e.g. git)
type ExecResult struct {
	returnCode int
	output     string
}

func main() {

	var (
		configFile       = flag.String("config", "", "which config file to use")
		envBranchFlag    = flag.String("branch", "", "which git branch of the Puppet environment to update, e.g. core_foobar")
		pfFlag           = flag.Bool("puppetfile", false, "install all modules from Puppetfile in cwd")
		forceFlag        = flag.Bool("force", false, "purge the Puppet environment directory and do a full sync")
		dryRunFlag       = flag.Bool("dryrun", false, "do not modify anything, just print what would be changed")
		usemoveFlag      = flag.Bool("usemove", false, "do not use hardlinks to populate your Puppet environments with Puppetlabs Forge modules. Instead uses simple move commands and purges the Forge cache directory after each run! (Useful for g10k runs inside a Docker container)")
		check4updateFlag = flag.Bool("check4update", false, "only check if the is newer version of the Puppet module avaialable. Does implicitly set dryrun to true")
		checkSumFlag     = flag.Bool("checksum", false, "get the md5 check sum for each Puppetlabs Forge module and verify the integrity of the downloaded archive. Increases g10k run time!")
		debugFlag        = flag.Bool("debug", false, "log debug output, defaults to false")
		verboseFlag      = flag.Bool("verbose", false, "log verbose output, defaults to false")
		infoFlag         = flag.Bool("info", false, "log info output, defaults to false")
		versionFlag      = flag.Bool("version", false, "show build time and version number")
	)
	flag.Parse()

	debug = *debugFlag
	verbose = *verboseFlag
	info = *infoFlag
	force = *forceFlag
	dryRun = *dryRunFlag
	check4update = *check4updateFlag
	usemove = *usemoveFlag
	pfMode = *pfFlag
	checkSum = *checkSumFlag

	if *versionFlag {
		fmt.Println("g10k Version 1.0 Build time:", buildtime, "UTC")
		os.Exit(0)
	}

	if check4update {
		dryRun = true
	}

	// check for git executable dependency
	if _, err := exec.LookPath("git"); err != nil {
		Fatalf("Error: could not find 'git' executable in PATH")
	}

	target := ""
	before := time.Now()
	if len(*configFile) > 0 {
		if usemove {
			Fatalf("Error: -usemove parameter is only allowed in -puppetfile mode!")
		}
		if pfMode {
			Fatalf("Error: -puppetfile parameter is not allowed with -config parameter!")
		}
		Debugf("Using as config file: " + *configFile)
		config = readConfigfile(*configFile)
		target = *configFile
		if len(*envBranchFlag) > 0 {
			resolvePuppetEnvironment(*envBranchFlag)
			target += " with branch " + *envBranchFlag
		} else {
			resolvePuppetEnvironment("")
		}
	} else {
		if pfMode {
			Debugf("Trying to use as Puppetfile: ./Puppetfile")
			sm := make(map[string]Source)
			sm["cmdlineparam"] = Source{Basedir: "."}
			cachedir := "/tmp/g10k"
			if len(os.Getenv("g10k_cachedir")) > 0 {
				cachedir = os.Getenv("g10k_cachedir")
				cachedir = checkDirAndCreate(cachedir, "cachedir environment variable g10k_cachedir")
				Debugf("Found environment variable g10k_cachedir set to: " + cachedir)
			} else {
				cachedir = checkDirAndCreate(cachedir, "cachedir default value")
			}
			//config = ConfigSettings{CacheDir: cachedir, ForgeCacheDir: cachedir, ModulesCacheDir: cachedir, EnvCacheDir: cachedir, Forge:{Baseurl: "https://forgeapi.puppetlabs.com"}, Sources: sm}
			forgeDefaultSettings := Forge{Baseurl: "https://forgeapi.puppetlabs.com"}
			config = ConfigSettings{CacheDir: cachedir, ForgeCacheDir: cachedir, ModulesCacheDir: cachedir, EnvCacheDir: cachedir, Sources: sm, Forge: forgeDefaultSettings}
			target = "./Puppetfile"
			puppetfile := readPuppetfile("./Puppetfile", "", "cmdlineparam", false)
			puppetfile.workDir = "."
			pfm := make(map[string]Puppetfile)
			pfm["cmdlineparam"] = puppetfile
			resolvePuppetfile(pfm)
		} else {
			Fatalf("Error: you need to specify at least a config file or use the Puppetfile mode\nExample call: " + os.Args[0] + " -config test.yaml or " + os.Args[0] + " -puppetfile\n")
		}
	}

	if usemove {
		// we can not reuse the Forge cache at all when -usemove gets used, because we can not delete the -latest link for some reason
		defer purgeDir(config.ForgeCacheDir, "main() -puppetfile mode with -usemove parameter")
	}

	// DEBUG
	//pf := make(map[string]Puppetfile)
	//pf["core_fullmanaged"] = readPuppetfile("/tmp/core/core_fullmanaged/", "/home/andpaul/dev/go/src/github.com/xorpaul/g10k/portal_envs")
	//pf["itodsi_corosync"] = readPuppetfile("/tmp/itodsi/itodsi_corosync/", "/home/andpaul/dev/go/src/github.com/xorpaul/g10k/portal_envs")
	//resolvePuppetfile(pf)
	//resolveGitRepositories(config)
	//resolveForgeModules(configSettings.forge)
	//doModuleInstallOrNothing("camptocamp-postfix-1.2.2", "/tmp/g10k/camptocamp-postfix-1.2.2")
	//doModuleInstallOrNothing("saz-resolv_conf-latest")
	//readModuleMetadata("/tmp/g10k/forge/camptocamp-postfix-1.2.2/metadata.json")

	Debugf("Forge response JSON parsing took " + strconv.FormatFloat(forgeJsonParseTime, 'f', 4, 64) + " seconds")
	Debugf("Forge modules metadata.json parsing took " + strconv.FormatFloat(metadataJsonParseTime, 'f', 4, 64) + " seconds")

	if !check4update {
		fmt.Println("Synced", target, "with", syncGitCount, "git repositories and", syncForgeCount, "Forge modules in "+strconv.FormatFloat(time.Since(before).Seconds(), 'f', 1, 64)+"s with git ("+strconv.FormatFloat(syncGitTime, 'f', 1, 64)+"s sync, I/O", strconv.FormatFloat(ioGitTime, 'f', 1, 64)+"s) and Forge ("+strconv.FormatFloat(syncForgeTime, 'f', 1, 64)+"s query+download, I/O", strconv.FormatFloat(ioForgeTime, 'f', 1, 64)+"s)")
	}
	if dryRun && (needSyncForgeCount > 0 || needSyncGitCount > 0) {
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package app

import (
	"net/http"
	goruntime "runtime"

	genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
	apirequest "k8s.io/apiserver/pkg/endpoints/request"
	apiserver "k8s.io/apiserver/pkg/server"
	genericfilters "k8s.io/apiserver/pkg/server/filters"
	"k8s.io/apiserver/pkg/server/healthz"
	"k8s.io/apiserver/pkg/server/mux"
	"k8s.io/apiserver/pkg/server/routes"
	"k8s.io/client-go/kubernetes/scheme"
	componentbaseconfig "k8s.io/component-base/config"
	"k8s.io/component-base/configz"
	"k8s.io/component-base/logs"
	"k8s.io/component-base/metrics/legacyregistry"
	_ "k8s.io/component-base/metrics/prometheus/workqueue" // for workqueue metric registration
)

// BuildHandlerChain builds a handler chain with a base handler and CompletedConfig.
func BuildHandlerChain(apiHandler http.Handler, authorizationInfo *apiserver.AuthorizationInfo, authenticationInfo *apiserver.AuthenticationInfo) http.Handler {
	requestInfoResolver := &apirequest.RequestInfoFactory{}
	failedHandler := genericapifilters.Unauthorized(scheme.Codecs)

	handler := apiHandler
	if authorizationInfo != nil {
		handler = genericapifilters.WithAuthorization(apiHandler, authorizationInfo.Authorizer, scheme.Codecs)
	}
	if authenticationInfo != nil {
		handler = genericapifilters.WithAuthentication(handler, authenticationInfo.Authenticator, failedHandler, nil)
	}
	handler = genericapifilters.WithRequestInfo(handler, requestInfoResolver)
	handler = genericapifilters.WithCacheControl(handler)
	handler = genericfilters.WithHTTPLogging(handler)
	handler = genericfilters.WithPanicRecovery(handler, requestInfoResolver)

	return handler
}

// NewBaseHandler takes in CompletedConfig and returns a handler.
func NewBaseHandler(c *componentbaseconfig.DebuggingConfiguration, checks ...healthz.HealthChecker) *mux.PathRecorderMux {
	mux := mux.NewPathRecorderMux("controller-manager")
	healthz.InstallHandler(mux, checks...)
	if c.EnableProfiling {
		routes.Profiling{}.Install(mux)
		if c.EnableContentionProfiling {
			goruntime.SetBlockProfileRate(1)
		}
		routes.DebugFlags{}.Install(mux, "v", routes.StringFlagPutHandler(logs.GlogSetter))
	}
	configz.InstallHandler(mux)
	//lint:ignore SA1019 See the Metrics Stability Migration KEP
	mux.Handle("/metrics", legacyregistry.Handler())

	return mux
}
<commit_msg>use controller healthz<commit_after>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package app

import (
	"net/http"
	goruntime "runtime"

	genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
	apirequest "k8s.io/apiserver/pkg/endpoints/request"
	apiserver "k8s.io/apiserver/pkg/server"
	genericfilters "k8s.io/apiserver/pkg/server/filters"
	"k8s.io/apiserver/pkg/server/mux"
	"k8s.io/apiserver/pkg/server/routes"
	"k8s.io/client-go/kubernetes/scheme"
	componentbaseconfig "k8s.io/component-base/config"
	"k8s.io/component-base/configz"
	"k8s.io/component-base/logs"
	"k8s.io/component-base/metrics/legacyregistry"
	_ "k8s.io/component-base/metrics/prometheus/workqueue" // for workqueue metric registration
)

// BuildHandlerChain builds a handler chain with a base handler and CompletedConfig.
func BuildHandlerChain(apiHandler http.Handler, authorizationInfo *apiserver.AuthorizationInfo, authenticationInfo *apiserver.AuthenticationInfo) http.Handler {
	requestInfoResolver := &apirequest.RequestInfoFactory{}
	failedHandler := genericapifilters.Unauthorized(scheme.Codecs)

	handler := apiHandler
	if authorizationInfo != nil {
		handler = genericapifilters.WithAuthorization(apiHandler, authorizationInfo.Authorizer, scheme.Codecs)
	}
	if authenticationInfo != nil {
		handler = genericapifilters.WithAuthentication(handler, authenticationInfo.Authenticator, failedHandler, nil)
	}
	handler = genericapifilters.WithRequestInfo(handler, requestInfoResolver)
	handler = genericapifilters.WithCacheControl(handler)
	handler = genericfilters.WithHTTPLogging(handler)
	handler = genericfilters.WithPanicRecovery(handler, requestInfoResolver)

	return handler
}

// NewBaseHandler takes in CompletedConfig and returns a handler.
func NewBaseHandler(c *componentbaseconfig.DebuggingConfiguration, healthzHandler http.Handler) *mux.PathRecorderMux {
	mux := mux.NewPathRecorderMux("controller-manager")
	mux.Handle("/healthz", healthzHandler)
	if c.EnableProfiling {
		routes.Profiling{}.Install(mux)
		if c.EnableContentionProfiling {
			goruntime.SetBlockProfileRate(1)
		}
		routes.DebugFlags{}.Install(mux, "v", routes.StringFlagPutHandler(logs.GlogSetter))
	}
	configz.InstallHandler(mux)
	//lint:ignore SA1019 See the Metrics Stability Migration KEP
	mux.Handle("/metrics", legacyregistry.Handler())

	return mux
}
<|endoftext|>


<commit_before>// Copyright Praegressus Limited. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !plan9,!solaris

package main

import (
	"encoding/binary"
	"flag"
	"fmt"
	"hash/fnv"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"gopkg.in/fsnotify.v1"
	"gopkg.in/yaml.v2"
)

const atomicThreshold = 250 * time.Millisecond

var (
	config     *configuration
	logFile    *os.File
	watcher    *fsnotify.Watcher
	events     = map[uint64]time.Time{}
	matches    map[uint64]*match
	rules      map[fsnotify.Op][]*rule
	eventsMu   = &sync.Mutex{}
	matchesMu  = &sync.RWMutex{}
	rulesMu    = &sync.RWMutex{}
	configFile = flag.String("config", ".gawp", "Configuration file")
	hasher64   = fnv.New64a()
	hasher64Mu = &sync.Mutex{}
)

// Gawp configuration
type configuration struct {
	recursive, verbose bool
	workers            int
	atomicThreshold    time.Duration
	logFile            string
}

type rule struct {
	match *regexp.Regexp
	cmds  []string
}

type match struct {
	mu   *sync.Mutex
	rule *rule
	cmds []string
}

func main() {
	flag.Parse()
	log.SetFlags(log.Ldate | log.Lmicroseconds)

	dir, err := os.Getwd()

	if err != nil {
		log.Fatal(err)
	}

	defer logFile.Close()

	if err = load(dir, *configFile); err != nil {
		log.Fatalf("unable to load configuration file: %s (%s)", *configFile, err)
	}

	// File system notifications
	if watcher, err = fsnotify.NewWatcher(); err != nil {
		log.Fatal(err)
	}

	defer watcher.Close()

	if config.recursive {
		// Watch root and child directories
		if err = filepath.Walk(dir, walk); err != nil {
			log.Fatal(err)
		}
	} else if err = watcher.Add(dir); err != nil {
		//  Only watch the root dir
		log.Fatal(err)
	}

	log.Println("started Gawp")

	// Disable file system notifications for the log file
	if config.logFile != "" {
		if err = watcher.Remove(config.logFile); err != nil {
			log.Println(err)
		}
	}

	var (
		signals  = make(chan os.Signal, 2)             // OS signal capture
		throttle = make(chan struct{}, config.workers) // Worker throttle
		wg       = &sync.WaitGroup{}
	)

	// Handle signals for clean shutdown
	signal.Notify(signals, os.Interrupt, syscall.SIGTERM)

	// Wait for workers on shutdown
	defer wg.Wait()

	for {
		select {
		case event := <-watcher.Events:
			filename := event.Name[len(dir)+1:]

			if isAtomicOp(event.Op, filename) {
				continue
			}

			throttle <- struct{}{}

			if config.verbose {
				log.Println(event.String())
			}

			// Reload config file
			if filename == *configFile {
				// Wait for active workers
				wg.Wait()
				log.Println("reloading config file")

				l, w := config.logFile, config.workers

				if err = load(dir, filename); err != nil {
					log.Fatal(err)
				}

				if config.logFile != "" && config.logFile != l {
					if err = watcher.Remove(config.logFile); err != nil {
						log.Println(err)
					}
				}

				<-throttle

				if config.workers != w {
					throttle = make(chan struct{}, config.workers)
				}

				continue
			}

			// Add or stop watching directories
			if err = handleEvent(event); err != nil {
				log.Println(err)

				continue
			}

			wg.Add(1)

			go worker(throttle, wg, event.Op, filename)

		case err = <-watcher.Errors:
			log.Println("fsnotify error:", err)

		case <-signals:
			return
		}
	}
}

// isAtomicOp attempts to detect atomic operations
func isAtomicOp(e fsnotify.Op, f string) bool {
	eventsMu.Lock()

	defer eventsMu.Unlock()

	h := hash64(e, f)
	n := time.Now()

	if t, exists := events[h]; exists && n.Sub(t) <= config.atomicThreshold {
		return true
	}

	events[h] = n

	return false
}

func worker(throttle chan struct{}, wg *sync.WaitGroup, e fsnotify.Op, f string) {
	defer func() {
		<-throttle

		wg.Done()
	}()

	m := findMatch(e, f)

	if m == nil {
		return
	}

	// Atomicity for the given match
	(*m).mu.Lock()

	defer (*m).mu.Unlock()

	for i, c := range m.cmds {
		b, err := cmd(c)

		if err != nil {
			log.Printf("command (%s) error: %s", c, err)
		} else if len(b) > 0 {
			log.Printf("%s\n%s", m.rule.cmds[i], b)
		}
	}
}

// walk implements filepath.WalkFunc; adding each directory
// to the file system notifications watcher
func walk(path string, f os.FileInfo, err error) error {
	// Ignore files
	if !f.IsDir() {
		return nil
	}

	// Ignore hidden directories
	if f.Name()[0] == '.' {
		return filepath.SkipDir
	}

	if err := watcher.Add(path); err != nil {
		log.Printf("unable to watch path: %s (%s)", path, err)
	}

	return nil
}

// handleEvent determines the nature of the event, adding
// or removing directories to the file system notifications watcher
func handleEvent(e fsnotify.Event) error {
	if e.Op&fsnotify.Create != fsnotify.Create {
		return nil
	}

	s, err := os.Stat(e.Name)

	if err != nil {
		return err
	} else if !s.IsDir() {
		return nil
	}

	if config.recursive {
		return filepath.Walk(e.Name+"/", walk)
	}

	return watcher.Add(e.Name)
}

// findMatch attempts to find a rule match for file path
// On success caches the match for fast future lookups
func findMatch(e fsnotify.Op, f string) *match {
	matchesMu.Lock()

	defer matchesMu.Unlock()

	h := hash64(e, f)

	// Fast map lookup, circumvent regular expressions
	c, exists := matches[h]

	if exists {
		return c
	}

	var m *match

	// Always cache for fast lookup
	defer func() {
		matches[h] = m
	}()

	rulesMu.RLock()

	defer rulesMu.RUnlock()

	// Check there's rules associated with the event type
	r, exists := rules[e]

	if !exists {
		return nil
	}

	// Test each rule for a match
	for _, c := range r {
		s := c.match.FindAllStringSubmatch(f, -1)

		if s == nil {
			continue
		}

		m = &match{mu: &sync.Mutex{}, rule: c, cmds: nil}

		for _, cmd := range c.cmds {
			for i := range s[0] {
				if i == 0 {
					continue
				}

				cmd = strings.Replace(cmd, "$"+strconv.Itoa(i), s[0][i], -1)
			}

			m.cmds = append(m.cmds, strings.Replace(cmd, "$file", f, -1))
		}

		break
	}

	return m
}

// load loads the Gawp config file and handles the loading of rules
func load(dir, f string) error {
	// Init/reset config, rules and matches cache
	config = &configuration{recursive: true}
	matches = map[uint64]*match{}
	rules = map[fsnotify.Op][]*rule{}

	// Open config file
	h, err := os.Open(dir + "/" + f)

	if err != nil {
		return err
	}

	defer h.Close()

	b, err := ioutil.ReadAll(h)

	if err != nil {
		return err
	}

	var c map[string]interface{}

	if err = yaml.Unmarshal(b, &c); err != nil {
		return err
	}

	for k, v := range c {
		// Conversions not tested for success; keep type defaults
		switch strings.ToLower(k) {
		case "recursive":
			config.recursive, _ = v.(bool)

		case "verbose":
			config.verbose, _ = v.(bool)

		case "workers":
			config.workers, _ = v.(int)

		case "atomicthreshold":
			i, _ := v.(int)

			if i > 0 {
				config.atomicThreshold = time.Duration(i)
			}

		case "logfile":
			config.logFile, _ = v.(string)

		default:
			if err = parseRules(k, v); err != nil {
				return err
			}
		}
	}

	// Default atomic threshold
	if config.atomicThreshold == 0 {
		config.atomicThreshold = atomicThreshold
	}

	// Determine operating system threads that can execute user-level Go code simultaneously
	if config.workers != 1 {
		switch n := runtime.NumCPU(); config.workers {
		case 0:
			if n >= 4 {
				config.workers = n / 2
			} else {
				config.workers = 1
			}

		default:
			if config.workers > n {
				config.workers = n
			}
		}
	}

	runtime.GOMAXPROCS(config.workers)

	if err = setLogFile(dir, config.logFile); err != nil {
		return err
	}

	return nil
}

// parseRules builds the rules map, adding rules into
// its defined event "bucket"
func parseRules(s string, i interface{}) error {
	e := parseEvents(s)

	if len(e) == 0 {
		return nil
	}

	var err error

	switch c := i.(type) {
	case map[interface{}]interface{}:
		for k, v := range c {
			// Regular expression
			m, ok := k.(string)

			if !ok {
				return nil
			}

			// Commands
			p, ok := v.([]interface{})

			if !ok || len(p) == 0 {
				return nil
			}

			r := &rule{}

			if r.match, err = regexp.Compile(m); err != nil {
				return fmt.Errorf("rule compilation error: %s (%s)", m, err)
			}

			for _, c := range p {
				cmd, ok := c.(string)

				if !ok || cmd == "" {
					continue
				}

				if cmd = strings.TrimSpace(cmd); cmd == "" {
					continue
				}

				r.cmds = append(r.cmds, cmd)
			}

			if len(r.cmds) == 0 {
				continue
			}

			// Add the rule to each event bucket
			for _, c := range e {
				rules[c] = append(rules[c], r)
			}
		}
	}

	return nil
}

// parseEvents returns the fsnotify.Op values
// for the events in the string
func parseEvents(s string) (e []fsnotify.Op) {
	for _, v := range strings.Split(s, ",") {
		switch strings.ToLower(strings.TrimSpace(v)) {
		case "create":
			e = append(e, fsnotify.Create)
		case "write":
			e = append(e, fsnotify.Write)
		case "rename":
			e = append(e, fsnotify.Rename)
		case "remove":
			e = append(e, fsnotify.Remove)
		case "chmod":
			e = append(e, fsnotify.Chmod)
		}
	}

	return
}

// setLogFile sets the logger output destination
func setLogFile(dir, f string) error {
	if f == "" {
		log.SetOutput(os.Stdout)

		return nil
	}

	// Relative path
	if f[0] != '/' {
		f = dir + "/" + f
	}

	// Force log file rotation, no side effects
	logFile.Close()

	var err error

	if logFile, err = os.OpenFile(f, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666); err != nil {
		return err
	}

	log.SetOutput(logFile)

	return nil
}

// hash64 returns the hash of the given FS operation & string as uint64
func hash64(e fsnotify.Op, s string) uint64 {
	hasher64Mu.Lock()

	defer hasher64Mu.Unlock()

	hasher64.Reset()
	binary.Write(hasher64, binary.LittleEndian, e)
	hasher64.Write([]byte(s))

	return hasher64.Sum64()
}
<commit_msg>- Add lock file - Minor code adjustments<commit_after>// Copyright Praegressus Limited. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !plan9,!solaris

package main

import (
	"encoding/binary"
	"flag"
	"fmt"
	"hash/fnv"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"gopkg.in/fsnotify.v1"
	"gopkg.in/yaml.v2"
)

const atomicThreshold = 250 * time.Millisecond

var (
	config     *configuration
	logFile    *os.File
	watcher    *fsnotify.Watcher
	events     = map[uint64]time.Time{}
	matches    map[uint64]*match
	rules      map[fsnotify.Op][]*rule
	eventsMu   = &sync.Mutex{}
	matchesMu  = &sync.RWMutex{}
	rulesMu    = &sync.RWMutex{}
	configFile = flag.String("config", ".gawp", "Configuration file")
	hasher64   = fnv.New64a()
	hasher64Mu = &sync.Mutex{}
)

// Gawp configuration
type configuration struct {
	recursive, verbose bool
	workers            int
	atomicThreshold    time.Duration
	logFile            string
}

type rule struct {
	match *regexp.Regexp
	cmds  []string
}

type match struct {
	mu   *sync.Mutex
	rule *rule
	cmds []string
}

func main() {
	flag.Parse()
	log.SetFlags(log.Ldate | log.Lmicroseconds)

	dir, err := os.Getwd()

	if err != nil {
		log.Fatal(err)
	}

	lock, err := lockDir(dir)

	if err != nil {
		log.Fatalf("unable to lock active directory: %s", err)
	}

	defer func() {
		if err := os.Remove(lock.Name()); err != nil {
			log.Println(err)
		}

		if logFile == nil {
			return
		} else if err := logFile.Close(); err != nil {
			log.Println(err)
		}
	}()

	if err = load(dir, *configFile); err != nil {
		log.Fatalf("unable to load configuration file: %s (%s)", *configFile, err)
	}

	// File system notifications
	if watcher, err = fsnotify.NewWatcher(); err != nil {
		log.Fatal(err)
	}

	defer watcher.Close()

	if config.recursive {
		// Watch root and child directories
		if err = filepath.Walk(dir, walk); err != nil {
			log.Fatal(err)
		}
	} else if err = watcher.Add(dir); err != nil {
		//  Only watch the root dir
		log.Fatal(err)
	}

	log.Println("started Gawp")

	// Disable file system notifications for the log file
	if config.logFile != "" {
		if err = watcher.Remove(config.logFile); err != nil {
			log.Println(err)
		}
	}

	var (
		signals  = make(chan os.Signal, 1)             // OS signal capture
		throttle = make(chan struct{}, config.workers) // Worker throttle
		wg       = &sync.WaitGroup{}
	)

	// Handle signals for clean shutdown
	signal.Notify(signals, os.Interrupt, syscall.SIGTERM)

	// Wait for workers on shutdown
	defer wg.Wait()

	for {
		select {
		case event := <-watcher.Events:
			filename := event.Name[len(dir)+1:]

			if isAtomicOp(event.Op, filename) {
				continue
			}

			throttle <- struct{}{}

			if config.verbose {
				log.Println(event.String())
			}

			// Reload config file
			if filename == *configFile {
				// Wait for active workers
				wg.Wait()
				log.Println("reloading config file")

				l, w := config.logFile, config.workers

				if err = load(dir, filename); err != nil {
					log.Fatal(err)
				}

				if config.logFile != "" && config.logFile != l {
					if err = watcher.Remove(config.logFile); err != nil {
						log.Println(err)
					}
				}

				<-throttle

				if config.workers != w {
					throttle = make(chan struct{}, config.workers)
				}

				continue
			}

			// Add or stop watching directories
			if err = handleEvent(event); err != nil {
				log.Println(err)

				continue
			}

			wg.Add(1)

			go worker(throttle, wg, event.Op, filename)

		case err = <-watcher.Errors:
			log.Println("fsnotify error:", err)

		case <-signals:
			return
		}
	}
}

// isAtomicOp attempts to detect atomic operations
func isAtomicOp(e fsnotify.Op, f string) bool {
	eventsMu.Lock()

	defer eventsMu.Unlock()

	h := hash64(e, f)
	n := time.Now()

	if t, exists := events[h]; exists && n.Sub(t) <= config.atomicThreshold {
		return true
	}

	events[h] = n

	return false
}

func worker(throttle chan struct{}, wg *sync.WaitGroup, e fsnotify.Op, f string) {
	defer func() {
		<-throttle

		wg.Done()
	}()

	m := findMatch(e, f)

	if m == nil {
		return
	}

	// Atomicity for the given match
	(*m).mu.Lock()

	defer (*m).mu.Unlock()

	for i, c := range m.cmds {
		if b, err := cmd(c); err != nil {
			log.Printf("command (%s) error: %s", c, err)
		} else if len(b) > 0 {
			log.Printf("%s\n%s", m.rule.cmds[i], b)
		}
	}
}

// walk implements filepath.WalkFunc; adding each directory
// to the file system notifications watcher
func walk(path string, f os.FileInfo, err error) error {
	if !f.IsDir() {
		// Ignore files
		return nil
	} else if f.Name()[0] == '.' {
		// Ignore hidden directories
		return filepath.SkipDir
	} else if err := watcher.Add(path); err != nil {
		log.Printf("unable to watch path: %s (%s)", path, err)
	}

	return nil
}

// handleEvent determines the nature of the event, adding
// or removing directories to the file system notifications watcher
func handleEvent(e fsnotify.Event) error {
	if e.Op&fsnotify.Create != fsnotify.Create {
		return nil
	}

	if s, err := os.Stat(e.Name); err != nil {
		return err
	} else if !s.IsDir() {
		return nil
	} else if config.recursive {
		return filepath.Walk(e.Name+"/", walk)
	}

	return watcher.Add(e.Name)
}

// findMatch attempts to find a rule match for file path
// On success caches the match for fast future lookups
func findMatch(e fsnotify.Op, f string) *match {
	matchesMu.Lock()

	defer matchesMu.Unlock()

	h := hash64(e, f)

	// Fast map lookup, circumvent regular expressions
	c, exists := matches[h]

	if exists {
		return c
	}

	var m *match

	// Always cache for fast lookup
	defer func() {
		matches[h] = m
	}()

	rulesMu.RLock()

	defer rulesMu.RUnlock()

	// Check there's rules associated with the event type
	r, exists := rules[e]

	if !exists {
		return nil
	}

	// Test each rule for a match
	for _, c := range r {
		s := c.match.FindAllStringSubmatch(f, -1)

		if s == nil {
			continue
		}

		m = &match{mu: &sync.Mutex{}, rule: c, cmds: nil}

		for _, cmd := range c.cmds {
			for i := range s[0] {
				if i == 0 {
					continue
				}

				cmd = strings.Replace(cmd, "$"+strconv.Itoa(i), s[0][i], -1)
			}

			m.cmds = append(m.cmds, strings.Replace(cmd, "$file", f, -1))
		}

		break
	}

	return m
}

// load loads the Gawp config file and handles the loading of rules
func load(dir, f string) error {
	// Init/reset config, rules and matches cache
	config = &configuration{recursive: true}
	matches = map[uint64]*match{}
	rules = map[fsnotify.Op][]*rule{}

	// Open config file
	h, err := os.Open(dir + "/" + f)

	if err != nil {
		return err
	}

	defer h.Close()

	b, err := ioutil.ReadAll(h)

	if err != nil {
		return err
	}

	var c map[string]interface{}

	if err = yaml.Unmarshal(b, &c); err != nil {
		return err
	}

	for k, v := range c {
		// Conversions not tested for success; keep type defaults
		switch strings.ToLower(k) {
		case "recursive":
			config.recursive, _ = v.(bool)

		case "verbose":
			config.verbose, _ = v.(bool)

		case "workers":
			config.workers, _ = v.(int)

		case "atomicthreshold":
			if i, _ := v.(int); i > 0 {
				config.atomicThreshold = time.Duration(i)
			}

		case "logfile":
			config.logFile, _ = v.(string)

		default:
			if err = parseRules(k, v); err != nil {
				return err
			}
		}
	}

	// Default atomic threshold
	if config.atomicThreshold == 0 {
		config.atomicThreshold = atomicThreshold
	}

	// Determine operating system threads that can execute user-level Go code simultaneously
	if config.workers != 1 {
		switch n := runtime.NumCPU(); config.workers {
		case 0:
			if n >= 4 {
				config.workers = n / 2
			} else {
				config.workers = 1
			}

		default:
			if config.workers > n {
				config.workers = n
			}
		}
	}

	runtime.GOMAXPROCS(config.workers)

	if err = setLogFile(dir, config.logFile); err != nil {
		return err
	}

	return nil
}

// parseRules builds the rules map, adding rules into
// its defined event "bucket"
func parseRules(s string, i interface{}) error {
	e := parseEvents(s)

	if len(e) == 0 {
		return nil
	}

	var err error

	switch c := i.(type) {
	case map[interface{}]interface{}:
		for k, v := range c {
			// Regular expression
			m, ok := k.(string)

			if !ok {
				return nil
			}

			// Commands
			p, ok := v.([]interface{})

			if !ok || len(p) == 0 {
				return nil
			}

			r := &rule{}

			if r.match, err = regexp.Compile(m); err != nil {
				return fmt.Errorf("rule compilation error: %s (%s)", m, err)
			}

			for _, c := range p {
				cmd, ok := c.(string)

				if !ok || cmd == "" {
					continue
				} else if cmd = strings.TrimSpace(cmd); cmd == "" {
					continue
				}

				r.cmds = append(r.cmds, cmd)
			}

			if len(r.cmds) == 0 {
				continue
			}

			// Add the rule to each event bucket
			for _, c := range e {
				rules[c] = append(rules[c], r)
			}
		}
	}

	return nil
}

// parseEvents returns the fsnotify.Op values
// for the events in the string
func parseEvents(s string) (e []fsnotify.Op) {
	for _, v := range strings.Split(s, ",") {
		switch strings.ToLower(strings.TrimSpace(v)) {
		case "create":
			e = append(e, fsnotify.Create)
		case "write":
			e = append(e, fsnotify.Write)
		case "rename":
			e = append(e, fsnotify.Rename)
		case "remove":
			e = append(e, fsnotify.Remove)
		case "chmod":
			e = append(e, fsnotify.Chmod)
		}
	}

	return
}

// setLogFile sets the logger output destination
func setLogFile(dir, f string) error {
	if f == "" {
		log.SetOutput(os.Stdout)

		return nil
	}

	// Relative path
	if f[0] != '/' {
		f = dir + "/" + f
	}

	// Force log file rotation, no side effects
	logFile.Close()

	var err error

	if logFile, err = os.OpenFile(f, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666); err != nil {
		return err
	}

	log.SetOutput(logFile)

	return nil
}

// lockDir creates a lock file for the active directory
func lockDir(dir string) (*os.File, error) {
	f := os.TempDir() + "/gawp-" + strconv.FormatUint(hash64(0, dir), 36) + ".lock"

	return os.OpenFile(f, os.O_CREATE|os.O_EXCL, os.ModeExclusive)
}

// hash64 returns the hash of the given FS operation & string as uint64
func hash64(e fsnotify.Op, s string) uint64 {
	hasher64Mu.Lock()

	defer hasher64Mu.Unlock()

	hasher64.Reset()
	binary.Write(hasher64, binary.LittleEndian, e)
	hasher64.Write([]byte(s))

	return hasher64.Sum64()
}
<|endoftext|>


<commit_before>// Copyright 2012 Daniel Connelly.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rtreego

import (
	"fmt"
	"math"
	"strings"
)

// DimError represents a failure due to mismatched dimensions.
type DimError struct {
	Expected int
	Actual   int
}

func (err DimError) Error() string {
	return "rtreego: dimension mismatch"
}

// DistError is an improper distance measurement.  It implements the error
// and is generated when a distance-related assertion fails.
type DistError float64

func (err DistError) Error() string {
	return "rtreego: improper distance"
}

// Point represents a point in n-dimensional Euclidean space.
type Point []float64

// Dist computes the Euclidean distance between two points p and q.
func (p Point) dist(q Point) float64 {
	if len(p) != len(q) {
		panic(DimError{len(p), len(q)})
	}
	sum := 0.0
	for i := range p {
		dx := p[i] - q[i]
		sum += dx * dx
	}
	return math.Sqrt(sum)
}

// minDist computes the square of the distance from a point to a rectangle.
// If the point is contained in the rectangle then the distance is zero.
//
// Implemented per Definition 2 of "Nearest Neighbor Queries" by
// N. Roussopoulos, S. Kelley and F. Vincent, ACM SIGMOD, pages 71-79, 1995.
func (p Point) minDist(r Rect) float64 {
	if len(p) != len(r.p) {
		panic(DimError{len(p), len(r.p)})
	}

	sum := 0.0
	for i, pi := range p {
		if pi < r.p[i] {
			d := pi - r.p[i]
			sum += d * d
		} else if pi > r.q[i] {
			d := pi - r.q[i]
			sum += d * d
		} else {
			sum += 0
		}
	}
	return sum
}

// minMaxDist computes the minimum of the maximum distances from p to points
// on r.  If r is the bounding box of some geometric objects, then there is
// at least one object contained in r within minMaxDist(p, r) of p.
//
// Implemented per Definition 4 of "Nearest Neighbor Queries" by
// N. Roussopoulos, S. Kelley and F. Vincent, ACM SIGMOD, pages 71-79, 1995.
func (p Point) minMaxDist(r Rect) float64 {
	if len(p) != len(r.p) {
		panic(DimError{len(p), len(r.p)})
	}

	// by definition, MinMaxDist(p, r) =
	// min{1<=k<=n}(|pk - rmk|^2 + sum{1<=i<=n, i != k}(|pi - rMi|^2))
	// where rmk and rMk are defined as follows:

	rm := func(k int) float64 {
		if p[k] <= (r.p[k]+r.q[k])/2 {
			return r.p[k]
		}
		return r.q[k]
	}

	rM := func(k int) float64 {
		if p[k] >= (r.p[k]+r.q[k])/2 {
			return r.p[k]
		}
		return r.q[k]
	}

	// This formula can be computed in linear time by precomputing
	// S = sum{1<=i<=n}(|pi - rMi|^2).

	S := 0.0
	for i := range p {
		d := p[i] - rM(i)
		S += d * d
	}

	// Compute MinMaxDist using the precomputed S.
	min := math.MaxFloat64
	for k := range p {
		d1 := p[k] - rM(k)
		d2 := p[k] - rm(k)
		d := S - d1*d1 + d2*d2
		if d < min {
			min = d
		}
	}

	return min
}

// Rect represents a subset of n-dimensional Euclidean space of the form
// [a1, b1] x [a2, b2] x ... x [an, bn], where ai < bi for all 1 <= i <= n.
type Rect struct {
	p, q Point // Enforced by NewRect: p[i] <= q[i] for all i.
}

// PointCoord returns the coordinate of the point of the rectangle at i
func (r Rect) PointCoord(i int) float64 {
	return r.p[i]
}

// LengthsCoord returns the coordinate of the lengths of the rectangle at i
func (r Rect) LengthsCoord(i int) float64 {
	return r.q[i] - r.p[i]
}

// Equal returns true if the two rectangles are equal
func (r Rect) Equal(other Rect) bool {
	for i, e := range r.p {
		if e != other.p[i] {
			return false
		}
	}
	for i, e := range r.q {
		if e != other.q[i] {
			return false
		}
	}
	return true
}

func (r Rect) String() string {
	s := make([]string, len(r.p))
	for i, a := range r.p {
		b := r.q[i]
		s[i] = fmt.Sprintf("[%.2f, %.2f]", a, b)
	}
	return strings.Join(s, "x")
}

// NewRect constructs and returns a pointer to a Rect given a corner point and
// the lengths of each dimension.  The point p should be the most-negative point
// on the rectangle (in every dimension) and every length should be positive.
func NewRect(p Point, lengths []float64) (r Rect, err error) {
	// r = new(Rect)
	r.p = p
	if len(p) != len(lengths) {
		err = &DimError{len(p), len(lengths)}
		return
	}
	r.q = make([]float64, len(p))
	for i := range p {
		if lengths[i] <= 0 {
			err = DistError(lengths[i])
			return
		}
		r.q[i] = p[i] + lengths[i]
	}
	return
}

// NewRectFromPoints constructs and returns a pointer to a Rect given a corner points.
func NewRectFromPoints(minPoint, maxPoint Point) (r Rect, err error) {
	if len(minPoint) != len(maxPoint) {
		err = &DimError{len(minPoint), len(maxPoint)}
		return
	}

	//checking that  min and max points is swapping
	for i, p := range minPoint {
		if minPoint[i] > maxPoint[i] {
			minPoint[i] = maxPoint[i]
			maxPoint[i] = p
		}
	}

	r = Rect{p: minPoint, q: maxPoint}
	return
}

// Size computes the measure of a rectangle (the product of its side lengths).
func (r Rect) Size() float64 {
	size := 1.0
	for i, a := range r.p {
		b := r.q[i]
		size *= b - a
	}
	return size
}

// margin computes the sum of the edge lengths of a rectangle.
func (r Rect) margin() float64 {
	// The number of edges in an n-dimensional rectangle is n * 2^(n-1)
	// (http://en.wikipedia.org/wiki/Hypercube_graph).  Thus the number
	// of edges of length (ai - bi), where the rectangle is determined
	// by p = (a1, a2, ..., an) and q = (b1, b2, ..., bn), is 2^(n-1).
	//
	// The margin of the rectangle, then, is given by the formula
	// 2^(n-1) * [(b1 - a1) + (b2 - a2) + ... + (bn - an)].
	dim := len(r.p)
	sum := 0.0
	for i, a := range r.p {
		b := r.q[i]
		sum += b - a
	}
	return math.Pow(2, float64(dim-1)) * sum
}

// containsPoint tests whether p is located inside or on the boundary of r.
func (r Rect) containsPoint(p Point) bool {
	if len(p) != len(r.p) {
		panic(DimError{len(r.p), len(p)})
	}

	for i, a := range p {
		// p is contained in (or on) r if and only if p <= a <= q for
		// every dimension.
		if a < r.p[i] || a > r.q[i] {
			return false
		}
	}

	return true
}

// containsRect tests whether r2 is is located inside r1.
func (r Rect) containsRect(r2 Rect) bool {
	if len(r.p) != len(r2.p) {
		panic(DimError{len(r.p), len(r2.p)})
	}

	for i, a1 := range r.p {
		b1, a2, b2 := r.q[i], r2.p[i], r2.q[i]
		// enforced by constructor: a1 <= b1 and a2 <= b2.
		// so containment holds if and only if a1 <= a2 <= b2 <= b1
		// for every dimension.
		if a1 > a2 || b2 > b1 {
			return false
		}
	}

	return true
}

// intersect computes the intersection of two rectangles.  If no intersection
// exists, the intersection is nil.
func intersect(r1, r2 Rect) bool {
	dim := len(r1.p)
	if len(r2.p) != dim {
		panic(DimError{dim, len(r2.p)})
	}

	// There are four cases of overlap:
	//
	//     1.  a1------------b1
	//              a2------------b2
	//              p--------q
	//
	//     2.       a1------------b1
	//         a2------------b2
	//              p--------q
	//
	//     3.  a1-----------------b1
	//              a2-------b2
	//              p--------q
	//
	//     4.       a1-------b1
	//         a2-----------------b2
	//              p--------q
	//
	// Thus there are only two cases of non-overlap:
	//
	//     1. a1------b1
	//                    a2------b2
	//
	//     2.             a1------b1
	//        a2------b2
	//
	// Enforced by constructor: a1 <= b1 and a2 <= b2.  So we can just
	// check the endpoints.

	for i := range r1.p {
		a1, b1, a2, b2 := r1.p[i], r1.q[i], r2.p[i], r2.q[i]
		if b2 <= a1 || b1 <= a2 {
			return false
		}
	}
	return true
}

// ToRect constructs a rectangle containing p with side lengths 2*tol.
func (p Point) ToRect(tol float64) Rect {
	dim := len(p)
	a, b := make([]float64, dim), make([]float64, dim)
	for i := range p {
		a[i] = p[i] - tol
		b[i] = p[i] + tol
	}
	return Rect{a, b}
}

// boundingBox constructs the smallest rectangle containing both r1 and r2.
func boundingBox(r1, r2 Rect) (bb Rect) {
	// bb = new(Rect)
	dim := len(r1.p)
	bb.p = make([]float64, dim)
	bb.q = make([]float64, dim)
	if len(r2.p) != dim {
		panic(DimError{dim, len(r2.p)})
	}
	for i := 0; i < dim; i++ {
		if r1.p[i] <= r2.p[i] {
			bb.p[i] = r1.p[i]
		} else {
			bb.p[i] = r2.p[i]
		}
		if r1.q[i] <= r2.q[i] {
			bb.q[i] = r2.q[i]
		} else {
			bb.q[i] = r1.q[i]
		}
	}
	return
}

// boundingBoxN constructs the smallest rectangle containing all of r...
func boundingBoxN(rects ...Rect) (bb Rect) {
	if len(rects) == 1 {
		bb = rects[0]
		return
	}
	bb = boundingBox(rects[0], rects[1])
	for _, rect := range rects[2:] {
		bb = boundingBox(bb, rect)
	}
	return
}
<commit_msg>delete unneeded lines<commit_after>// Copyright 2012 Daniel Connelly.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rtreego

import (
	"fmt"
	"math"
	"strings"
)

// DimError represents a failure due to mismatched dimensions.
type DimError struct {
	Expected int
	Actual   int
}

func (err DimError) Error() string {
	return "rtreego: dimension mismatch"
}

// DistError is an improper distance measurement.  It implements the error
// and is generated when a distance-related assertion fails.
type DistError float64

func (err DistError) Error() string {
	return "rtreego: improper distance"
}

// Point represents a point in n-dimensional Euclidean space.
type Point []float64

// Dist computes the Euclidean distance between two points p and q.
func (p Point) dist(q Point) float64 {
	if len(p) != len(q) {
		panic(DimError{len(p), len(q)})
	}
	sum := 0.0
	for i := range p {
		dx := p[i] - q[i]
		sum += dx * dx
	}
	return math.Sqrt(sum)
}

// minDist computes the square of the distance from a point to a rectangle.
// If the point is contained in the rectangle then the distance is zero.
//
// Implemented per Definition 2 of "Nearest Neighbor Queries" by
// N. Roussopoulos, S. Kelley and F. Vincent, ACM SIGMOD, pages 71-79, 1995.
func (p Point) minDist(r Rect) float64 {
	if len(p) != len(r.p) {
		panic(DimError{len(p), len(r.p)})
	}

	sum := 0.0
	for i, pi := range p {
		if pi < r.p[i] {
			d := pi - r.p[i]
			sum += d * d
		} else if pi > r.q[i] {
			d := pi - r.q[i]
			sum += d * d
		} else {
			sum += 0
		}
	}
	return sum
}

// minMaxDist computes the minimum of the maximum distances from p to points
// on r.  If r is the bounding box of some geometric objects, then there is
// at least one object contained in r within minMaxDist(p, r) of p.
//
// Implemented per Definition 4 of "Nearest Neighbor Queries" by
// N. Roussopoulos, S. Kelley and F. Vincent, ACM SIGMOD, pages 71-79, 1995.
func (p Point) minMaxDist(r Rect) float64 {
	if len(p) != len(r.p) {
		panic(DimError{len(p), len(r.p)})
	}

	// by definition, MinMaxDist(p, r) =
	// min{1<=k<=n}(|pk - rmk|^2 + sum{1<=i<=n, i != k}(|pi - rMi|^2))
	// where rmk and rMk are defined as follows:

	rm := func(k int) float64 {
		if p[k] <= (r.p[k]+r.q[k])/2 {
			return r.p[k]
		}
		return r.q[k]
	}

	rM := func(k int) float64 {
		if p[k] >= (r.p[k]+r.q[k])/2 {
			return r.p[k]
		}
		return r.q[k]
	}

	// This formula can be computed in linear time by precomputing
	// S = sum{1<=i<=n}(|pi - rMi|^2).

	S := 0.0
	for i := range p {
		d := p[i] - rM(i)
		S += d * d
	}

	// Compute MinMaxDist using the precomputed S.
	min := math.MaxFloat64
	for k := range p {
		d1 := p[k] - rM(k)
		d2 := p[k] - rm(k)
		d := S - d1*d1 + d2*d2
		if d < min {
			min = d
		}
	}

	return min
}

// Rect represents a subset of n-dimensional Euclidean space of the form
// [a1, b1] x [a2, b2] x ... x [an, bn], where ai < bi for all 1 <= i <= n.
type Rect struct {
	p, q Point // Enforced by NewRect: p[i] <= q[i] for all i.
}

// PointCoord returns the coordinate of the point of the rectangle at i
func (r Rect) PointCoord(i int) float64 {
	return r.p[i]
}

// LengthsCoord returns the coordinate of the lengths of the rectangle at i
func (r Rect) LengthsCoord(i int) float64 {
	return r.q[i] - r.p[i]
}

// Equal returns true if the two rectangles are equal
func (r Rect) Equal(other Rect) bool {
	for i, e := range r.p {
		if e != other.p[i] {
			return false
		}
	}
	for i, e := range r.q {
		if e != other.q[i] {
			return false
		}
	}
	return true
}

func (r Rect) String() string {
	s := make([]string, len(r.p))
	for i, a := range r.p {
		b := r.q[i]
		s[i] = fmt.Sprintf("[%.2f, %.2f]", a, b)
	}
	return strings.Join(s, "x")
}

// NewRect constructs and returns a pointer to a Rect given a corner point and
// the lengths of each dimension.  The point p should be the most-negative point
// on the rectangle (in every dimension) and every length should be positive.
func NewRect(p Point, lengths []float64) (r Rect, err error) {
	r.p = p
	if len(p) != len(lengths) {
		err = &DimError{len(p), len(lengths)}
		return
	}
	r.q = make([]float64, len(p))
	for i := range p {
		if lengths[i] <= 0 {
			err = DistError(lengths[i])
			return
		}
		r.q[i] = p[i] + lengths[i]
	}
	return
}

// NewRectFromPoints constructs and returns a pointer to a Rect given a corner points.
func NewRectFromPoints(minPoint, maxPoint Point) (r Rect, err error) {
	if len(minPoint) != len(maxPoint) {
		err = &DimError{len(minPoint), len(maxPoint)}
		return
	}

	//checking that  min and max points is swapping
	for i, p := range minPoint {
		if minPoint[i] > maxPoint[i] {
			minPoint[i] = maxPoint[i]
			maxPoint[i] = p
		}
	}

	r = Rect{p: minPoint, q: maxPoint}
	return
}

// Size computes the measure of a rectangle (the product of its side lengths).
func (r Rect) Size() float64 {
	size := 1.0
	for i, a := range r.p {
		b := r.q[i]
		size *= b - a
	}
	return size
}

// margin computes the sum of the edge lengths of a rectangle.
func (r Rect) margin() float64 {
	// The number of edges in an n-dimensional rectangle is n * 2^(n-1)
	// (http://en.wikipedia.org/wiki/Hypercube_graph).  Thus the number
	// of edges of length (ai - bi), where the rectangle is determined
	// by p = (a1, a2, ..., an) and q = (b1, b2, ..., bn), is 2^(n-1).
	//
	// The margin of the rectangle, then, is given by the formula
	// 2^(n-1) * [(b1 - a1) + (b2 - a2) + ... + (bn - an)].
	dim := len(r.p)
	sum := 0.0
	for i, a := range r.p {
		b := r.q[i]
		sum += b - a
	}
	return math.Pow(2, float64(dim-1)) * sum
}

// containsPoint tests whether p is located inside or on the boundary of r.
func (r Rect) containsPoint(p Point) bool {
	if len(p) != len(r.p) {
		panic(DimError{len(r.p), len(p)})
	}

	for i, a := range p {
		// p is contained in (or on) r if and only if p <= a <= q for
		// every dimension.
		if a < r.p[i] || a > r.q[i] {
			return false
		}
	}

	return true
}

// containsRect tests whether r2 is is located inside r1.
func (r Rect) containsRect(r2 Rect) bool {
	if len(r.p) != len(r2.p) {
		panic(DimError{len(r.p), len(r2.p)})
	}

	for i, a1 := range r.p {
		b1, a2, b2 := r.q[i], r2.p[i], r2.q[i]
		// enforced by constructor: a1 <= b1 and a2 <= b2.
		// so containment holds if and only if a1 <= a2 <= b2 <= b1
		// for every dimension.
		if a1 > a2 || b2 > b1 {
			return false
		}
	}

	return true
}

// intersect computes the intersection of two rectangles.  If no intersection
// exists, the intersection is nil.
func intersect(r1, r2 Rect) bool {
	dim := len(r1.p)
	if len(r2.p) != dim {
		panic(DimError{dim, len(r2.p)})
	}

	// There are four cases of overlap:
	//
	//     1.  a1------------b1
	//              a2------------b2
	//              p--------q
	//
	//     2.       a1------------b1
	//         a2------------b2
	//              p--------q
	//
	//     3.  a1-----------------b1
	//              a2-------b2
	//              p--------q
	//
	//     4.       a1-------b1
	//         a2-----------------b2
	//              p--------q
	//
	// Thus there are only two cases of non-overlap:
	//
	//     1. a1------b1
	//                    a2------b2
	//
	//     2.             a1------b1
	//        a2------b2
	//
	// Enforced by constructor: a1 <= b1 and a2 <= b2.  So we can just
	// check the endpoints.

	for i := range r1.p {
		a1, b1, a2, b2 := r1.p[i], r1.q[i], r2.p[i], r2.q[i]
		if b2 <= a1 || b1 <= a2 {
			return false
		}
	}
	return true
}

// ToRect constructs a rectangle containing p with side lengths 2*tol.
func (p Point) ToRect(tol float64) Rect {
	dim := len(p)
	a, b := make([]float64, dim), make([]float64, dim)
	for i := range p {
		a[i] = p[i] - tol
		b[i] = p[i] + tol
	}
	return Rect{a, b}
}

// boundingBox constructs the smallest rectangle containing both r1 and r2.
func boundingBox(r1, r2 Rect) (bb Rect) {
	dim := len(r1.p)
	bb.p = make([]float64, dim)
	bb.q = make([]float64, dim)
	if len(r2.p) != dim {
		panic(DimError{dim, len(r2.p)})
	}
	for i := 0; i < dim; i++ {
		if r1.p[i] <= r2.p[i] {
			bb.p[i] = r1.p[i]
		} else {
			bb.p[i] = r2.p[i]
		}
		if r1.q[i] <= r2.q[i] {
			bb.q[i] = r2.q[i]
		} else {
			bb.q[i] = r1.q[i]
		}
	}
	return
}

// boundingBoxN constructs the smallest rectangle containing all of r...
func boundingBoxN(rects ...Rect) (bb Rect) {
	if len(rects) == 1 {
		bb = rects[0]
		return
	}
	bb = boundingBox(rects[0], rects[1])
	for _, rect := range rects[2:] {
		bb = boundingBox(bb, rect)
	}
	return
}
<|endoftext|>


<commit_before>package glfw3

// Not sure about the darwin flag

// Windows users: If you download the GLFW 64-bit binaries, when you copy over the contents of the lib folder make sure to rename
// glfw3dll.a to libglfw3dll.a, it doesn't work otherwise.

//#cgo windows LDFLAGS: -lglfw3dll -lopengl32 -lgdi32
//#cgo linux LDFLAGS: -lglfw
//#cgo darwin LDFLAGS: -lglfw3 -framework Cocoa -framework OpenGL -framework IOKit
//#ifdef _WIN32
//  #define GLFW_DLL
//#endif
//#include <GLFW/glfw3.h>
import "C"

const (
	VersionMajor    = C.GLFW_VERSION_MAJOR    //This is incremented when the API is changed in non-compatible ways.
	VersionMinor    = C.GLFW_VERSION_MINOR    //This is incremented when features are added to the API but it remains backward-compatible.
	VersionRevision = C.GLFW_VERSION_REVISION //This is incremented when a bug fix release is made that does not contain any API changes.
)

//Init initializes the GLFW library. Before most GLFW functions can be used,
//GLFW must be initialized, and before a program terminates GLFW should be
//terminated in order to free any resources allocated during or after
//initialization.
//
//If this function fails, it calls Terminate before returning. If it succeeds,
//you should call Terminate before the program exits.
//
//Additional calls to this function after successful initialization but before
//termination will succeed but will do nothing.
//
//This function may take several seconds to complete on some systems, while on
//other systems it may take only a fraction of a second to complete.
//
//On Mac OS X, this function will change the current directory of the
//application to the Contents/Resources subdirectory of the application's
//bundle, if present.
//
//This function may only be called from the main thread. See
//https://code.google.com/p/go-wiki/wiki/LockOSThread
func Init() bool {
	return glfwbool(C.glfwInit())
}

//Terminate destroys all remaining windows, frees any allocated resources and
//sets the library to an uninitialized state. Once this is called, you must
//again call Init successfully before you will be able to use most GLFW
//functions.
//
//If GLFW has been successfully initialized, this function should be called
//before the program exits. If initialization fails, there is no need to call
//this function, as it is called by Init before it returns failure.
//
//This function may only be called from the main thread. See
//https://code.google.com/p/go-wiki/wiki/LockOSThread
func Terminate() {
	C.glfwTerminate()
}

//GetVersion retrieves the major, minor and revision numbers of the GLFW
//library. It is intended for when you are using GLFW as a shared library and
//want to ensure that you are using the minimum required version.
//
//This function may be called before Init.
func GetVersion() (major, minor, revision int) {
	var (
		maj C.int
		min C.int
		rev C.int
	)

	C.glfwGetVersion(&maj, &min, &rev)
	return int(maj), int(min), int(rev)
}

//GetVersionString returns a static string generated at compile-time according
//to which configuration macros were defined. This is intended for use when
//submitting bug reports, to allow developers to see which code paths are
//enabled in a binary.
//
//This function may be called before Init.
func GetVersionString() string {
	return C.GoString(C.glfwGetVersionString())
}
<commit_msg>Added a dependency in Darwin for framework CoreVideo required by GLFW as of 3.0.4<commit_after>package glfw3

// Not sure about the darwin flag

// Windows users: If you download the GLFW 64-bit binaries, when you copy over the contents of the lib folder make sure to rename
// glfw3dll.a to libglfw3dll.a, it doesn't work otherwise.

//#cgo windows LDFLAGS: -lglfw3dll -lopengl32 -lgdi32
//#cgo linux LDFLAGS: -lglfw
//#cgo darwin LDFLAGS: -lglfw3 -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo
//#ifdef _WIN32
//  #define GLFW_DLL
//#endif
//#include <GLFW/glfw3.h>
import "C"

const (
	VersionMajor    = C.GLFW_VERSION_MAJOR    //This is incremented when the API is changed in non-compatible ways.
	VersionMinor    = C.GLFW_VERSION_MINOR    //This is incremented when features are added to the API but it remains backward-compatible.
	VersionRevision = C.GLFW_VERSION_REVISION //This is incremented when a bug fix release is made that does not contain any API changes.
)

//Init initializes the GLFW library. Before most GLFW functions can be used,
//GLFW must be initialized, and before a program terminates GLFW should be
//terminated in order to free any resources allocated during or after
//initialization.
//
//If this function fails, it calls Terminate before returning. If it succeeds,
//you should call Terminate before the program exits.
//
//Additional calls to this function after successful initialization but before
//termination will succeed but will do nothing.
//
//This function may take several seconds to complete on some systems, while on
//other systems it may take only a fraction of a second to complete.
//
//On Mac OS X, this function will change the current directory of the
//application to the Contents/Resources subdirectory of the application's
//bundle, if present.
//
//This function may only be called from the main thread. See
//https://code.google.com/p/go-wiki/wiki/LockOSThread
func Init() bool {
	return glfwbool(C.glfwInit())
}

//Terminate destroys all remaining windows, frees any allocated resources and
//sets the library to an uninitialized state. Once this is called, you must
//again call Init successfully before you will be able to use most GLFW
//functions.
//
//If GLFW has been successfully initialized, this function should be called
//before the program exits. If initialization fails, there is no need to call
//this function, as it is called by Init before it returns failure.
//
//This function may only be called from the main thread. See
//https://code.google.com/p/go-wiki/wiki/LockOSThread
func Terminate() {
	C.glfwTerminate()
}

//GetVersion retrieves the major, minor and revision numbers of the GLFW
//library. It is intended for when you are using GLFW as a shared library and
//want to ensure that you are using the minimum required version.
//
//This function may be called before Init.
func GetVersion() (major, minor, revision int) {
	var (
		maj C.int
		min C.int
		rev C.int
	)

	C.glfwGetVersion(&maj, &min, &rev)
	return int(maj), int(min), int(rev)
}

//GetVersionString returns a static string generated at compile-time according
//to which configuration macros were defined. This is intended for use when
//submitting bug reports, to allow developers to see which code paths are
//enabled in a binary.
//
//This function may be called before Init.
func GetVersionString() string {
	return C.GoString(C.glfwGetVersionString())
}
<|endoftext|>


<commit_before>package gomq

import (
	"net"
	"strings"
	"time"

	"github.com/zeromq/gomq/zmtp"
)

var (
	defaultRetry = 250 * time.Millisecond
)

// Connection is a gomq connection. It holds
// both the net.Conn transport as well as the
// zmtp connection information.
type Connection struct {
	net  net.Conn
	zmtp *zmtp.Connection
}

// ZeroMQSocket is the base gomq interface.
type ZeroMQSocket interface {
	Recv() ([]byte, error)
	Send([]byte) error
	RetryInterval() time.Duration
	SocketType() zmtp.SocketType
	SecurityMechanism() zmtp.SecurityMechanism
	AddConnection(conn *Connection)
	RecvChannel() chan *zmtp.Message
	Close()
}

// Client is a gomq interface used for client sockets.
// It implements the Socket interface along with a
// Connect method for connecting to endpoints.
type Client interface {
	ZeroMQSocket
	Connect(endpoint string) error
}

// ConnectClient accepts a Client interface and an endpoint
// in the format <proto>://<address>:<port>. It then attempts
// to connect to the endpoint and perform a ZMTP handshake.
func ConnectClient(c Client, endpoint string) error {
	parts := strings.Split(endpoint, "://")

Connect:
	netConn, err := net.Dial(parts[0], parts[1])
	if err != nil {
		time.Sleep(c.RetryInterval())
		goto Connect
	}

	zmtpConn := zmtp.NewConnection(netConn)
	_, err = zmtpConn.Prepare(c.SecurityMechanism(), c.SocketType(), false, nil)
	if err != nil {
		return err
	}

	conn := &Connection{
		net:  netConn,
		zmtp: zmtpConn,
	}

	c.AddConnection(conn)
	zmtpConn.Recv(c.RecvChannel())
	return nil
}

// Server is a gomq interface used for server sockets.
// It implements the Socket interface along with a
// Bind method for binding to endpoints.
type Server interface {
	ZeroMQSocket
	Bind(endpoint string) (net.Addr, error)
}

// BindServer accepts a Server interface and an endpoint
// in the format <proto>://<address>:<port>. It then attempts
// to bind to the endpoint. TODO: change this to starting
// a listener on the endpoint that performs handshakes
// with any client that connects
func BindServer(s Server, endpoint string) (net.Addr, error) {
	var addr net.Addr
	parts := strings.Split(endpoint, "://")

	ln, err := net.Listen(parts[0], parts[1])
	if err != nil {
		return addr, err
	}

	netConn, err := ln.Accept()
	if err != nil {
		return addr, err
	}

	zmtpConn := zmtp.NewConnection(netConn)
	_, err = zmtpConn.Prepare(s.SecurityMechanism(), s.SocketType(), true, nil)
	if err != nil {
		return netConn.LocalAddr(), err
	}

	conn := &Connection{
		net:  netConn,
		zmtp: zmtpConn,
	}

	s.AddConnection(conn)
	zmtpConn.Recv(s.RecvChannel())
	return netConn.LocalAddr(), nil
}
<commit_msg>Problem: ZeroMQSocket interface did not include RemoveConnection<commit_after>package gomq

import (
	"net"
	"strings"
	"time"

	"github.com/zeromq/gomq/zmtp"
)

var (
	defaultRetry = 250 * time.Millisecond
)

// Connection is a gomq connection. It holds
// both the net.Conn transport as well as the
// zmtp connection information.
type Connection struct {
	net  net.Conn
	zmtp *zmtp.Connection
}

// ZeroMQSocket is the base gomq interface.
type ZeroMQSocket interface {
	Recv() ([]byte, error)
	Send([]byte) error
	RetryInterval() time.Duration
	SocketType() zmtp.SocketType
	SecurityMechanism() zmtp.SecurityMechanism
	AddConnection(*Connection)
	RemoveConnection(string)
	RecvChannel() chan *zmtp.Message
	Close()
}

// Client is a gomq interface used for client sockets.
// It implements the Socket interface along with a
// Connect method for connecting to endpoints.
type Client interface {
	ZeroMQSocket
	Connect(endpoint string) error
}

// ConnectClient accepts a Client interface and an endpoint
// in the format <proto>://<address>:<port>. It then attempts
// to connect to the endpoint and perform a ZMTP handshake.
func ConnectClient(c Client, endpoint string) error {
	parts := strings.Split(endpoint, "://")

Connect:
	netConn, err := net.Dial(parts[0], parts[1])
	if err != nil {
		time.Sleep(c.RetryInterval())
		goto Connect
	}

	zmtpConn := zmtp.NewConnection(netConn)
	_, err = zmtpConn.Prepare(c.SecurityMechanism(), c.SocketType(), false, nil)
	if err != nil {
		return err
	}

	conn := &Connection{
		net:  netConn,
		zmtp: zmtpConn,
	}

	c.AddConnection(conn)
	zmtpConn.Recv(c.RecvChannel())
	return nil
}

// Server is a gomq interface used for server sockets.
// It implements the Socket interface along with a
// Bind method for binding to endpoints.
type Server interface {
	ZeroMQSocket
	Bind(endpoint string) (net.Addr, error)
}

// BindServer accepts a Server interface and an endpoint
// in the format <proto>://<address>:<port>. It then attempts
// to bind to the endpoint. TODO: change this to starting
// a listener on the endpoint that performs handshakes
// with any client that connects
func BindServer(s Server, endpoint string) (net.Addr, error) {
	var addr net.Addr
	parts := strings.Split(endpoint, "://")

	ln, err := net.Listen(parts[0], parts[1])
	if err != nil {
		return addr, err
	}

	netConn, err := ln.Accept()
	if err != nil {
		return addr, err
	}

	zmtpConn := zmtp.NewConnection(netConn)
	_, err = zmtpConn.Prepare(s.SecurityMechanism(), s.SocketType(), true, nil)
	if err != nil {
		return netConn.LocalAddr(), err
	}

	conn := &Connection{
		net:  netConn,
		zmtp: zmtpConn,
	}

	s.AddConnection(conn)
	zmtpConn.Recv(s.RecvChannel())
	return netConn.LocalAddr(), nil
}
<|endoftext|>


<commit_before>// The algorithm implemented here is based on "An O(NP) Sequence Comparison Algorithm"
// by described by Sun Wu, Udi Manber and Gene Myers

package gonp

import (
	"container/list"
	"fmt"
	"math"
	"unicode/utf8"
)

type SesType int

const (
	Delete SesType = iota
	Common
	Add
)

type Point struct {
	x, y, k int
}

type SesElem struct {
	c rune
	t SesType
}

type Diff struct {
	A    []rune
	B    []rune
	m, n int
	ed   int
	ctl  *Ctl
	lcs  *list.List
	ses  *list.List
}

type Ctl struct {
	reverse  bool
	path     []int
	onlyEd   bool
	pathposi map[int]Point
}

func max(x, y int) int {
	return int(math.Max(float64(x), float64(y)))
}

func New(a string, b string) *Diff {
	m, n := utf8.RuneCountInString(a), utf8.RuneCountInString(b)
	diff := new(Diff)
	ctl := new(Ctl)
	if m >= n {
		diff.A, diff.B = []rune(b), []rune(a)
		diff.m, diff.n = n, m
		ctl.reverse = true
	} else {
		diff.A, diff.B = []rune(a), []rune(b)
		diff.m, diff.n = m, n
		ctl.reverse = false
	}
	ctl.onlyEd = false
	diff.ctl = ctl
	return diff
}

func (diff *Diff) OnlyEd() {
	diff.ctl.onlyEd = true
}

func (diff *Diff) Editdistance() int {
	return diff.ed
}

func (diff *Diff) Lcs() string {
	var b = make([]rune, diff.lcs.Len())
	for i, e := 0, diff.lcs.Front(); e != nil; i, e = i+1, e.Next() {
		b[i] = e.Value.(rune)
	}
	return string(b)
}

func (diff *Diff) Ses() []SesElem {
	seq := make([]SesElem, diff.ses.Len())
	for i, e := 0, diff.ses.Front(); e != nil; i, e = i+1, e.Next() {
		seq[i].c = e.Value.(SesElem).c
		seq[i].t = e.Value.(SesElem).t
	}
	return seq
}

func (diff *Diff) PrintSes(add, del, common string) {
	for _, e := 0, diff.ses.Front(); e != nil; e = e.Next() {
		ee := e.Value.(SesElem)
		switch ee.t {
		case Delete:
			fmt.Println(del + " " + string(ee.c))
		case Add:
			fmt.Println(add + " " + string(ee.c))
		case Common:
			fmt.Println(common + " " + string(ee.c))
		}
	}
}

func (diff *Diff) Compose() {
	offset := diff.m + 1
	delta := diff.n - diff.m
	size := diff.m + diff.n + 3
	fp := make([]int, size)
	diff.ctl.path = make([]int, size)
	diff.ctl.pathposi = make(map[int]Point)
	diff.lcs = list.New()
	diff.ses = list.New()
	ctl := diff.ctl

	for i := range fp {
		fp[i] = -1
		ctl.path[i] = -1
	}

	for p := 0; ; p++ {

		for k := -p; k <= delta-1; k++ {
			fp[k+offset] = diff.snake(k, fp[k-1+offset]+1, fp[k+1+offset], offset, diff.ctl)
		}

		for k := delta + p; k >= delta+1; k-- {
			fp[k+offset] = diff.snake(k, fp[k-1+offset]+1, fp[k+1+offset], offset, diff.ctl)
		}

		fp[delta+offset] = diff.snake(delta, fp[delta-1+offset]+1, fp[delta+1+offset], offset, diff.ctl)

		if fp[delta+offset] >= diff.n {
			diff.ed = delta + 2*p
			break
		}
	}

	if ctl.onlyEd {
		return
	}

	r := ctl.path[delta+offset]
	epc := make(map[int]Point)
	for r != -1 {
		epc[len(epc)] = Point{x: ctl.pathposi[r].x, y: ctl.pathposi[r].y, k: -1}
		r = ctl.pathposi[r].k
	}
	diff.recordSeq(epc)
}

func (diff *Diff) recordSeq(epc map[int]Point) {
	x_idx, y_idx := 1, 1
	px_idx, py_idx := 0, 0
	ctl := diff.ctl
	for i := len(epc) - 1; i >= 0; i-- {
		for (px_idx < epc[i].x) || (py_idx < epc[i].y) {
			var t SesType
			if (epc[i].y - epc[i].x) > (py_idx - px_idx) {
				elem := diff.B[py_idx]
				if ctl.reverse {
					t = Delete
				} else {
					t = Add
				}
				diff.ses.PushBack(SesElem{c: elem, t: t})
				y_idx++
				py_idx++
			} else if epc[i].y-epc[i].x < py_idx-px_idx {
				elem := diff.A[px_idx]
				if ctl.reverse {
					t = Add
				} else {
					t = Delete
				}
				diff.ses.PushBack(SesElem{c: elem, t: t})
				x_idx++
				px_idx++
			} else {
				elem := diff.A[px_idx]
				t = Common
				diff.lcs.PushBack(elem)
				diff.ses.PushBack(SesElem{c: elem, t: t})
				x_idx++
				y_idx++
				px_idx++
				py_idx++
			}
		}
	}
}

func (diff *Diff) snake(k, p, pp, offset int, ctl *Ctl) int {
	r := 0
	if p > pp {
		r = ctl.path[k-1+offset]
	} else {
		r = ctl.path[k+1+offset]
	}

	y := max(p, pp)
	x := y - k

	for x < diff.m && y < diff.n && diff.A[x] == diff.B[y] {
		x++
		y++
	}

	if !ctl.onlyEd {
		ctl.path[k+offset] = len(ctl.pathposi)
		ctl.pathposi[len(ctl.pathposi)] = Point{x: x, y: y, k: r}
	}

	return y
}
<commit_msg>Not give arguments to PrintSes().<commit_after>// The algorithm implemented here is based on "An O(NP) Sequence Comparison Algorithm"
// by described by Sun Wu, Udi Manber and Gene Myers

package gonp

import (
	"container/list"
	"fmt"
	"math"
	"unicode/utf8"
)

type SesType int

const (
	Delete SesType = iota
	Common
	Add
)

type Point struct {
	x, y, k int
}

type SesElem struct {
	c rune
	t SesType
}

type Diff struct {
	A    []rune
	B    []rune
	m, n int
	ed   int
	ctl  *Ctl
	lcs  *list.List
	ses  *list.List
}

type Ctl struct {
	reverse  bool
	path     []int
	onlyEd   bool
	pathposi map[int]Point
}

func max(x, y int) int {
	return int(math.Max(float64(x), float64(y)))
}

func New(a string, b string) *Diff {
	m, n := utf8.RuneCountInString(a), utf8.RuneCountInString(b)
	diff := new(Diff)
	ctl := new(Ctl)
	if m >= n {
		diff.A, diff.B = []rune(b), []rune(a)
		diff.m, diff.n = n, m
		ctl.reverse = true
	} else {
		diff.A, diff.B = []rune(a), []rune(b)
		diff.m, diff.n = m, n
		ctl.reverse = false
	}
	ctl.onlyEd = false
	diff.ctl = ctl
	return diff
}

func (diff *Diff) OnlyEd() {
	diff.ctl.onlyEd = true
}

func (diff *Diff) Editdistance() int {
	return diff.ed
}

func (diff *Diff) Lcs() string {
	var b = make([]rune, diff.lcs.Len())
	for i, e := 0, diff.lcs.Front(); e != nil; i, e = i+1, e.Next() {
		b[i] = e.Value.(rune)
	}
	return string(b)
}

func (diff *Diff) Ses() []SesElem {
	seq := make([]SesElem, diff.ses.Len())
	for i, e := 0, diff.ses.Front(); e != nil; i, e = i+1, e.Next() {
		seq[i].c = e.Value.(SesElem).c
		seq[i].t = e.Value.(SesElem).t
	}
	return seq
}

func (diff *Diff) PrintSes() {
	for _, e := 0, diff.ses.Front(); e != nil; e = e.Next() {
		ee := e.Value.(SesElem)
		switch ee.t {
		case Delete:
			fmt.Println("- " + string(ee.c))
		case Add:
			fmt.Println("+ " + string(ee.c))
		case Common:
			fmt.Println("  " + string(ee.c))
		}
	}
}

func (diff *Diff) Compose() {
	offset := diff.m + 1
	delta := diff.n - diff.m
	size := diff.m + diff.n + 3
	fp := make([]int, size)
	diff.ctl.path = make([]int, size)
	diff.ctl.pathposi = make(map[int]Point)
	diff.lcs = list.New()
	diff.ses = list.New()
	ctl := diff.ctl

	for i := range fp {
		fp[i] = -1
		ctl.path[i] = -1
	}

	for p := 0; ; p++ {

		for k := -p; k <= delta-1; k++ {
			fp[k+offset] = diff.snake(k, fp[k-1+offset]+1, fp[k+1+offset], offset, diff.ctl)
		}

		for k := delta + p; k >= delta+1; k-- {
			fp[k+offset] = diff.snake(k, fp[k-1+offset]+1, fp[k+1+offset], offset, diff.ctl)
		}

		fp[delta+offset] = diff.snake(delta, fp[delta-1+offset]+1, fp[delta+1+offset], offset, diff.ctl)

		if fp[delta+offset] >= diff.n {
			diff.ed = delta + 2*p
			break
		}
	}

	if ctl.onlyEd {
		return
	}

	r := ctl.path[delta+offset]
	epc := make(map[int]Point)
	for r != -1 {
		epc[len(epc)] = Point{x: ctl.pathposi[r].x, y: ctl.pathposi[r].y, k: -1}
		r = ctl.pathposi[r].k
	}
	diff.recordSeq(epc)
}

func (diff *Diff) recordSeq(epc map[int]Point) {
	x_idx, y_idx := 1, 1
	px_idx, py_idx := 0, 0
	ctl := diff.ctl
	for i := len(epc) - 1; i >= 0; i-- {
		for (px_idx < epc[i].x) || (py_idx < epc[i].y) {
			var t SesType
			if (epc[i].y - epc[i].x) > (py_idx - px_idx) {
				elem := diff.B[py_idx]
				if ctl.reverse {
					t = Delete
				} else {
					t = Add
				}
				diff.ses.PushBack(SesElem{c: elem, t: t})
				y_idx++
				py_idx++
			} else if epc[i].y-epc[i].x < py_idx-px_idx {
				elem := diff.A[px_idx]
				if ctl.reverse {
					t = Add
				} else {
					t = Delete
				}
				diff.ses.PushBack(SesElem{c: elem, t: t})
				x_idx++
				px_idx++
			} else {
				elem := diff.A[px_idx]
				t = Common
				diff.lcs.PushBack(elem)
				diff.ses.PushBack(SesElem{c: elem, t: t})
				x_idx++
				y_idx++
				px_idx++
				py_idx++
			}
		}
	}
}

func (diff *Diff) snake(k, p, pp, offset int, ctl *Ctl) int {
	r := 0
	if p > pp {
		r = ctl.path[k-1+offset]
	} else {
		r = ctl.path[k+1+offset]
	}

	y := max(p, pp)
	x := y - k

	for x < diff.m && y < diff.n && diff.A[x] == diff.B[y] {
		x++
		y++
	}

	if !ctl.onlyEd {
		ctl.path[k+offset] = len(ctl.pathposi)
		ctl.pathposi[len(ctl.pathposi)] = Point{x: x, y: y, k: r}
	}

	return y
}
<|endoftext|>