Skip to content

Commit

Permalink
Display original words
Browse files Browse the repository at this point in the history
  • Loading branch information
mpppk committed Jun 30, 2019
1 parent cb88d4b commit 30b8263
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 63 deletions.
41 changes: 29 additions & 12 deletions cmd/gen.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package cmd

import (
"fmt"
"os"
"strings"

"github.com/mpppk/iroha/lib"
"github.com/spf13/cobra"
)
Expand All @@ -17,22 +21,35 @@ var genCmd = &cobra.Command{
panic(err)
}

normalizedWords := lib.NormalizeKatakanaWords(words)
iroha := lib.NewIroha(normalizedWords)
iroha.Search()
iroha := lib.NewIroha(words)
irohaWordsList := iroha.Search()
for _, irohaWords := range irohaWordsList {
if ok, _ := IsValidIroha(irohaWords); !ok {
fmt.Fprintln(os.Stderr, "invalid result is returned", irohaWords)
os.Exit(1)
}
fmt.Println(irohaWords)
}
fmt.Println(len(irohaWordsList))
},
}

func init() {
rootCmd.AddCommand(genCmd)
func IsValidIroha(words []string) (bool, string) {
concatenatedWord := strings.Join(words, "")
n := lib.NormalizeKatakanaWord(concatenatedWord)
runes := []rune(n)

// Here you will define your flags and configuration settings.
if len(runes) != int(lib.KatakanaLen) {
return false, n
}

// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// genCmd.PersistentFlags().String("foo", "", "A help for foo")
if lib.HasDuplicatedRune(n) {
return false, n
}

// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// genCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
return true, n
}

func init() {
rootCmd.AddCommand(genCmd)
}
46 changes: 26 additions & 20 deletions lib/iroha.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package lib

import (
"fmt"
"math/bits"
)

Expand All @@ -17,42 +16,49 @@ func NewIroha(words []string) *Iroha {
}
}

func (i *Iroha) Search() {
func (i *Iroha) Search() (wordStringsList [][]string) {
katakanaAndWordBitsList := i.katakana.ListSortedKatakanaAndWordBits()
res, _ := i.searchByBits(katakanaAndWordBitsList, WordBits(0))
fmt.Println(len(res))
wordsList, _ := i.searchByBits(katakanaAndWordBitsList, WordBits(0))
for _, words := range wordsList {
var wordStrings []string
for _, word := range words {
wordStrings = append(wordStrings, i.katakana.ToWord(word.Id))
}
wordStringsList = append(wordStringsList, wordStrings)
}
return
}

func (i *Iroha) searchByBits(katakanaAndWordBitsList []*KatakanaAndWordBits, remainKatakanaBits WordBits) ([][]WordBits, bool) {
if bits.OnesCount64(uint64(remainKatakanaBits)) == int(katakanaLen) {
return [][]WordBits{{}}, true
func (i *Iroha) searchByBits(katakanaBitsAndWords []*KatakanaBitsAndWords, remainKatakanaBits WordBits) ([][]*Word, bool) {
if bits.OnesCount64(uint64(remainKatakanaBits)) == int(KatakanaLen) {
return [][]*Word{{}}, true
}

if len(katakanaAndWordBitsList) == 0 {
if len(katakanaBitsAndWords) == 0 {
return nil, false
}

katakanaAndWordBits := katakanaAndWordBitsList[0]
var irohaWordBitsLists [][]WordBits
for _, wordBits := range katakanaAndWordBits.WordBitsList {
if remainKatakanaBits.HasDuplicatedKatakana(wordBits) {
katakanaAndWordBits := katakanaBitsAndWords[0]
var irohaWordLists [][]*Word
for _, word := range katakanaAndWordBits.Words {
if remainKatakanaBits.HasDuplicatedKatakana(word.Bits) {
continue
}
newRemainKatakanaBits := remainKatakanaBits.Merge(wordBits)
if newIrohaWordBitsLists, ok := i.searchByBits(katakanaAndWordBitsList[1:], newRemainKatakanaBits); ok {
for _, newIrohaWordBitsList := range newIrohaWordBitsLists {
newIrohaWordBitsList = append(newIrohaWordBitsList, wordBits)
irohaWordBitsLists = append(irohaWordBitsLists, newIrohaWordBitsList)
newRemainKatakanaBits := remainKatakanaBits.Merge(word.Bits)
if newIrohaWordIdLists, ok := i.searchByBits(katakanaBitsAndWords[1:], newRemainKatakanaBits); ok {
for _, newIrohaWordList := range newIrohaWordIdLists {
newIrohaWordList = append(newIrohaWordList, word)
irohaWordLists = append(irohaWordLists, newIrohaWordList)
}
}
}

// どれも入れない場合
if remainKatakanaBits.has(katakanaAndWordBits.KatakanaBits) {
if otherIrohaWordBitsLists, ok := i.searchByBits(katakanaAndWordBitsList[1:], remainKatakanaBits); ok {
irohaWordBitsLists = append(irohaWordBitsLists, otherIrohaWordBitsLists...)
if otherIrohaWordBitsLists, ok := i.searchByBits(katakanaBitsAndWords[1:], remainKatakanaBits); ok {
irohaWordLists = append(irohaWordLists, otherIrohaWordBitsLists...)
}
}

return irohaWordBitsLists, len(irohaWordBitsLists) > 0
return irohaWordLists, len(irohaWordLists) > 0
}
79 changes: 59 additions & 20 deletions lib/katakana.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import (
"strings"
)

type WordId uint16
type WordBits uint64
type Word struct {
Id WordId
Bits WordBits
}

func (w WordBits) has(katakanaBits KatakanaBits) bool {
return w&WordBits(katakanaBits) != 0
Expand All @@ -22,15 +27,16 @@ func (w WordBits) Merge(otherWordBits WordBits) WordBits {
type KatakanaBits uint64
type KatakanaBitsMap map[rune]KatakanaBits
type RKatakanaBitsMap map[KatakanaBits]rune
type WordBitsMap map[KatakanaBits][]WordBits
type WordByKatakanaMap map[KatakanaBits][]*Word
type WordMap map[WordId]string
type WordCountMap map[KatakanaBits]int
type KatakanaCount struct {
katakanaBits KatakanaBits
count int
}
type KatakanaAndWordBits struct {
type KatakanaBitsAndWords struct {
KatakanaBits KatakanaBits
WordBitsList []WordBits
Words []*Word
}

func (w WordCountMap) toSortedKatakanaBitsList() (katakanaBits []KatakanaBits) {
Expand Down Expand Up @@ -61,61 +67,79 @@ func (w WordCountMap) toList() []*KatakanaCount {
}

type Katakana struct {
katakanaBitsMap KatakanaBitsMap
wordBitsMap WordBitsMap
wordCountMap WordCountMap
katakanaBitsMap KatakanaBitsMap
wordByKatakanaMap WordByKatakanaMap
wordMap WordMap
wordCountMap WordCountMap
}

var katakanaLen = uint64(45)
var KatakanaLen = uint64(45)

func NewKatakana(words []string) *Katakana {
normalizedWords, orgWords := NormalizeAndFilterKatakanaWords(words)
katakana := &Katakana{
katakanaBitsMap: newKatakanaBitsMap(),
wordMap: toWordMap(orgWords),
}

wordBitsList := katakana.loadWords(words)
wordBitsList := katakana.loadWords(normalizedWords)
wordCountMap := countWordBitsFrequency(wordBitsList)
katakana.wordCountMap = wordCountMap
katakana.wordBitsMap = katakana.createWordBitsMap(wordBitsList)
katakana.wordByKatakanaMap = katakana.createWordBitsMap(wordBitsList)
return katakana
}

func (k *Katakana) ListSortedKatakanaAndWordBits() (katakanaAndWordBitsList []*KatakanaAndWordBits) {
func (k *Katakana) ListSortedKatakanaAndWordBits() (katakanaAndWordBitsList []*KatakanaBitsAndWords) {
katakanaBitsList := k.wordCountMap.toSortedKatakanaBitsList()
for _, katakanaBits := range katakanaBitsList {
katakanaAndWordBitsList = append(katakanaAndWordBitsList, &KatakanaAndWordBits{
katakanaAndWordBitsList = append(katakanaAndWordBitsList, &KatakanaBitsAndWords{
KatakanaBits: katakanaBits,
WordBitsList: k.wordBitsMap[katakanaBits],
Words: k.wordByKatakanaMap[katakanaBits],
})
}
return katakanaAndWordBitsList
}

func (k *Katakana) ToWord(wordId WordId) string {
return k.wordMap[wordId]
}

func (k *Katakana) loadWords(words []string) (wordBits []WordBits) {
for _, word := range words {
wordBits = append(wordBits, k.toWordBits(word))
}
return wordBits
}

func toWordMap(words []string) WordMap {
wordMap := WordMap{}
for wordId, word := range words {
wordMap[WordId(wordId)] = word
}
return wordMap
}

func (k *Katakana) toWordBits(word string) WordBits {
return toWordBits(k.katakanaBitsMap, word)
}

func (k *Katakana) createWordBitsMap(wordBitsList []WordBits) WordBitsMap {
func (k *Katakana) createWordBitsMap(wordBitsList []WordBits) WordByKatakanaMap {
sortedKatakanaBitsList := k.wordCountMap.toSortedKatakanaBitsList()
return newWordBitsMap(sortedKatakanaBitsList, wordBitsList)
}

func newWordBitsMap(sortedKatakanaBits []KatakanaBits, wordBitsList []WordBits) WordBitsMap {
func newWordBitsMap(sortedKatakanaBits []KatakanaBits, wordBitsList []WordBits) WordByKatakanaMap {
var newWordBitsList []WordBits
copy(newWordBitsList, wordBitsList)

wordBitsMap := WordBitsMap{}
for _, wordBits := range wordBitsList {
wordBitsMap := WordByKatakanaMap{}
for wordId, wordBits := range wordBitsList {
for _, katakanaBits := range sortedKatakanaBits {
if wordBits.has(katakanaBits) {
wordBitsMap[katakanaBits] = append(wordBitsMap[katakanaBits], wordBits)
wordBitsMap[katakanaBits] = append(wordBitsMap[katakanaBits], &Word{
Id: WordId(wordId),
Bits: wordBits,
})
break
}
}
Expand All @@ -134,7 +158,7 @@ func toWordBits(bitsMap KatakanaBitsMap, word string) WordBits {
func countWordBitsFrequency(wordBitsList []WordBits) WordCountMap {
wordCountMaps := WordCountMap{}
for _, wb := range wordBitsList {
for i := uint64(0); i < katakanaLen; i++ {
for i := uint64(0); i < KatakanaLen; i++ {
katakanaBits := KatakanaBits(1 << i)
if wb.has(katakanaBits) {
wordCountMaps[katakanaBits]++
Expand Down Expand Up @@ -207,9 +231,13 @@ func newNormalizeKatakanaMap() map[rune]rune {
return m
}

func NormalizeKatakanaWords(words []string) (newWords []string) {
func NormalizeAndFilterKatakanaWords(words []string) (normalizedWords, orgWords []string) {
for _, word := range words {
newWords = append(newWords, NormalizeKatakanaWord(word))
normalizedWord := NormalizeKatakanaWord(word)
if !HasDuplicatedRune(normalizedWord) {
normalizedWords = append(normalizedWords, normalizedWord)
orgWords = append(orgWords, word)
}
}
return
}
Expand All @@ -227,3 +255,14 @@ func NormalizeKatakanaWord(word string) string {
newWord := string(runes)
return strings.Replace(newWord, "ー", "", -1)
}

func HasDuplicatedRune(word string) bool {
m := map[rune]struct{}{}
for _, r := range word {
if _, ok := m[r]; ok {
return true
}
m[r] = struct{}{}
}
return false
}
22 changes: 11 additions & 11 deletions lib/katakana_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestNewKatakana(t *testing.T) {
katakanaBitsMap['ウ']: 2,
katakanaBitsMap['エ']: 1,
},
wordBitsMap: WordBitsMap{
wordByKatakanaMap: WordByKatakanaMap{
katakanaBitsMap['ア']: []WordBits{
toWordBits(katakanaBitsMap, "アイウ"),
},
Expand All @@ -46,8 +46,8 @@ func TestNewKatakana(t *testing.T) {
if !reflect.DeepEqual(katakana.wordCountMap, tt.want.wordCountMap) {
t.Errorf("wordCountMap() = %v, want %v", katakana.wordCountMap, tt.want.wordCountMap)
}
if !reflect.DeepEqual(katakana.wordBitsMap, tt.want.wordBitsMap) {
t.Errorf("wordBitsMap() = %v, want %v", katakana.wordBitsMap, tt.want.wordBitsMap)
if !reflect.DeepEqual(katakana.wordByKatakanaMap, tt.want.wordByKatakanaMap) {
t.Errorf("wordByKatakanaMap() = %v, want %v", katakana.wordByKatakanaMap, tt.want.wordByKatakanaMap)
}
})
}
Expand All @@ -57,13 +57,13 @@ func TestKatakana_ToSortedKatakanaAndWordBits(t *testing.T) {
katakanaBitsMap := newKatakanaBitsMap()
type fields struct {
katakanaBitsMap KatakanaBitsMap
wordBitsMap WordBitsMap
wordBitsMap WordByKatakanaMap
wordCountMap WordCountMap
}
tests := []struct {
name string
fields fields
wantKatakanaAndWordBitsList []*KatakanaAndWordBits
wantKatakanaAndWordBitsList []*KatakanaBitsAndWords
}{
{
name: "",
Expand All @@ -74,7 +74,7 @@ func TestKatakana_ToSortedKatakanaAndWordBits(t *testing.T) {
katakanaBitsMap['ウ']: 2,
katakanaBitsMap['エ']: 1,
},
wordBitsMap: WordBitsMap{
wordBitsMap: WordByKatakanaMap{
katakanaBitsMap['ア']: []WordBits{
toWordBits(katakanaBitsMap, "アイウ"),
},
Expand All @@ -84,7 +84,7 @@ func TestKatakana_ToSortedKatakanaAndWordBits(t *testing.T) {
},
katakanaBitsMap: katakanaBitsMap,
},
wantKatakanaAndWordBitsList: []*KatakanaAndWordBits{
wantKatakanaAndWordBitsList: []*KatakanaBitsAndWords{
{
KatakanaBits: katakanaBitsMap['ア'],
WordBitsList: []WordBits{
Expand All @@ -109,7 +109,7 @@ func TestKatakana_ToSortedKatakanaAndWordBits(t *testing.T) {
},
}

contains := func(list []*KatakanaAndWordBits, v *KatakanaAndWordBits) bool {
contains := func(list []*KatakanaBitsAndWords, v *KatakanaBitsAndWords) bool {
for _, nv := range list {
if nv.KatakanaBits == v.KatakanaBits {
// FIXME
Expand All @@ -125,9 +125,9 @@ func TestKatakana_ToSortedKatakanaAndWordBits(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
k := &Katakana{
katakanaBitsMap: tt.fields.katakanaBitsMap,
wordBitsMap: tt.fields.wordBitsMap,
wordCountMap: tt.fields.wordCountMap,
katakanaBitsMap: tt.fields.katakanaBitsMap,
wordByKatakanaMap: tt.fields.wordBitsMap,
wordCountMap: tt.fields.wordCountMap,
}

gotKatakanaAndWordBitsList := k.ListSortedKatakanaAndWordBits()
Expand Down

0 comments on commit 30b8263

Please sign in to comment.