package main

import (
    "fmt"
    "strings"
)

var encoded = 
    "MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH" +
    "VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD" +
    "ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS" +
    "FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG" +
    "ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ" +
    "ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS" +
    "JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT" +
    "LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST" +
    "MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH" +
    "QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV" +
    "RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW" +
    "TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO" +
    "SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR" +
    "ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX" +
    "BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB" +
    "BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA" +
    "FWAML ZZRXJ EKAHV FASMU LVVUT TGK"

var freq = [26]float64{
    0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015,
    0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749,
    0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758,
    0.00978, 0.02360, 0.00150, 0.01974, 0.00074,
}

func sum(a []float64) (sum float64) {
    for _, f := range a {
        sum += f
    }
    return
}

func bestMatch(a []float64) int {
    sum := sum(a)
    bestFit, bestRotate := 1e100, 0
    for rotate := 0; rotate < 26; rotate++ {
        fit := 0.0
        for i := 0; i < 26; i++ {
            d := a[(i+rotate)%26]/sum - freq[i]
            fit += d * d / freq[i]
        }
        if fit < bestFit {
            bestFit, bestRotate = fit, rotate
        }
    }
    return bestRotate
}

func freqEveryNth(msg []int, key []byte) float64 {
    l := len(msg)
    interval := len(key)
    out := make([]float64, 26)
    accu := make([]float64, 26)
    for j := 0; j < interval; j++ {
        for k := 0; k < 26; k++ {
            out[k] = 0.0
        }
        for i := j; i < l; i += interval {
            out[msg[i]]++
        }
        rot := bestMatch(out)
        key[j] = byte(rot + 65)
        for i := 0; i < 26; i++ {
            accu[i] += out[(i+rot)%26]
        }
    }
    sum := sum(accu)
    ret := 0.0
    for i := 0; i < 26; i++ {
        d := accu[i]/sum - freq[i]
        ret += d * d / freq[i]
    }
    return ret
}

func decrypt(text, key string) string {
    var sb strings.Builder
    ki := 0
    for _, c := range text {
        if c < 'A' || c > 'Z' {
            continue
        }
        ci := (c - rune(key[ki]) + 26) % 26
        sb.WriteRune(ci + 65)
        ki = (ki + 1) % len(key)
    }
    return sb.String()
}

func main() {
    enc := strings.Replace(encoded, " ", "", -1)
    txt := make([]int, len(enc))
    for i := 0; i < len(txt); i++ {
        txt[i] = int(enc[i] - 'A')
    }
    bestFit, bestKey := 1e100, ""
    fmt.Println("  Fit     Length   Key")
    for j := 1; j <= 26; j++ {
        key := make([]byte, j)
        fit := freqEveryNth(txt, key)
        sKey := string(key)
        fmt.Printf("%f   %2d    %s", fit, j, sKey)
        if fit < bestFit {
            bestFit, bestKey = fit, sKey
            fmt.Print(" <--- best so far")
        }
        fmt.Println()
    }
    fmt.Println("\nBest key:", bestKey)
    fmt.Printf("\nDecrypted text:\n%s\n", decrypt(enc, bestKey))
}