Любомир обнови решението на 31.12.2014 20:50 (преди над 3 години)
+package main
+
+import "sync"
+import "strconv"
+import "regexp"
+import "errors"
+
+const (
+ DOES_NOT_EXIST = iota
+ ENQUEUED
+ IN_PROGRESS
+ UNABLE_TO_PRODUCE
+ DONE
+)
+
+type Order struct {
+ Id string
+ Status int
+ Words []string
+ Result string
+ Channel chan *Order
+}
+
+var nextId int = 0
+
+func NewOrder(words []string, channel chan *Order) *Order {
+ nextId += 1
+ return &Order{
+ Id: strconv.Itoa(nextId),
+ Status: ENQUEUED,
+ Words: words,
+ Result: "",
+ Channel: channel,
+ }
+}
+
+type Factory struct {
+ sync.Mutex
+ materials map[string]uint16
+ orders chan *Order
+ stopChan chan struct{}
+ workers int
+}
+
+func NewFactory(workers uint8) *Factory {
+ return &Factory{
+ materials: make(map[string]uint16),
+ orders: make(chan *Order),
+ stopChan: make(chan struct{}, workers),
+ workers: int(workers),
+ }
+}
+
+func (f *Factory) Enqueue(order *Order) {
+ order.Status = ENQUEUED
+ go func() {
+ f.orders <- order
+ }()
+}
+
+func (f *Factory) StartProducing() {
+ for w := 0; w < f.workers; w += 1 {
+ go f.worker()
+ }
+}
+
+func (f *Factory) StopProducing() {
+ for w := 0; w < f.workers; w += 1 {
+ f.stopChan <- struct{}{}
+ }
+}
+
+func (f *Factory) StorageAdd(materials map[string]uint16) {
+ f.Lock()
+ defer f.Unlock()
+ for regex, count := range materials {
+ f.materials[regex] += count
+ }
+}
+
+func (f *Factory) worker() {
+ for {
+ select {
+ case <-f.stopChan:
+ return
+ case order := <-f.orders:
+ order.Status = IN_PROGRESS
+ if result, err := f.generateRegexp(order.Words); err == nil {
+ order.Result = result
+ order.Status = DONE
+ } else {
+ order.Status = UNABLE_TO_PRODUCE
+ }
+ go func() {
+ order.Channel <- order
+ }()
+ }
+ }
+}
+
+func getFirstMatch(words []string, parts map[string]uint16) (used []string) {
+ for part, count := range parts {
+ re, err := regexp.Compile("^" + part)
+ if err != nil {
+ continue
+ }
+
+ newWords := make([]string, 0)
+ for _, word := range words {
+ if re.FindStringIndex(word) == nil {
+ break
+ }
+ nword := re.ReplaceAllString(word, "")
+
+ newWords = append(newWords, nword)
+ }
+
+ if len(newWords) != len(words) {
+ continue
+ }
+
+ done := true
+ for _, word := range newWords {
+ done = done && word == ""
+ }
+ if done {
+ used := make([]string, 1)
+ used[0] = part
+ return used
+ }
+
+ leftParts := copyMap(parts)
+
+ if count == 1 {
+ delete(leftParts, part)
+ } else {
+ leftParts[part] = count - 1
+ }
+
+ used := getFirstMatch(newWords, leftParts)
+ if used != nil {
+ return append(used, part)
+ }
+ return nil
+ }
+
+ return nil
+}
+
+func reverseSlice(slice []string) {
+ for front, end := 0, len(slice)-1; front < end; front, end = front+1, end-1 {
+ slice[front], slice[end] = slice[end], slice[front]
+ }
+}
+
+func (f *Factory) generateRegexp(words []string) (result string, err error) {
+ f.Lock()
+ defer f.Unlock()
+ used := getFirstMatch(words, copyMap(f.materials))
+ reverseSlice(used)
+ if used != nil {
+ for _, word := range used {
+ result += word
+ if f.materials[word] == 1 {
+ delete(f.materials, word)
+ } else {
+ f.materials[word] -= 1
+ }
+ }
+ result = "^" + result + "$"
+ } else {
+ err = errors.New("Failed to match words")
+ }
+ return result, err
+}
+
+func copyMap(source map[string]uint16) map[string]uint16 {
+ dest := make(map[string]uint16)
+ for k, v := range source {
+ dest[k] = v
+ }
+ return dest
+}
+
+type Client struct {
+ Name string
+ Orders map[string]*Order
+ Channel chan *Order
+}
+
+func NewClient(name string) *Client {
+ return &Client{
+ Name: name,
+ Orders: make(map[string]*Order),
+ Channel: make(chan *Order),
+ }
+}
+
+func (c *Client) Order(factory *Factory, words []string) string {
+ order := NewOrder(words, c.Channel)
+ c.Orders[order.Id] = order
+ factory.Enqueue(order)
+ return order.Id
+}
+
+func (c *Client) CheckStatus(id string) int {
+ if order, ok := c.Orders[id]; ok {
+ return order.Status
+ }
+ return DOES_NOT_EXIST
+}