Problem:
知識の証明

あなたが住んでいる集合住宅の入口のドアには，パスワード式のロックがかけられている．このパスワードはちょうど 4 桁の 0 から 9 の範囲の数字で構成されており，あなたはいつも，集合住宅の管理人から伝えられたパスワード P を使って，このドアのロックを解除している．

ある日，あなたは集合住宅の住民全員が，自分と同じパスワード P を使っているのかどうか気になり，同じ集合住宅に住む友人に尋ねることにした．あなたとあなたの友人は，自分のパスワードを互いに伝えあうことで，同じパスワードを使っているのかどうか確かめ合うことができる．しかしながら，パスワードがそれぞれの住民に個別に割り当てられている可能性を考えると，この方法は好ましくない．自分のパスワードを知っているのは自分だけであるべきで，他人に伝えるべきではないからである．

この事態を防ぐために，あなたとあなたの友人は，自分のパスワードをハッシュ関数に入力し，得られたハッシュ値を互いに伝えあうことにした．ここで使用するハッシュ関数の計算式 S は小文字のアルファベット 'a', 'b', 'c', 'd' と記号 '[', ']', '+', '*', '^' からなり，以下のBNFで定義される <Hash> によって表される．

<Hash> ::= <Letter> | '['<Op><Hash><Hash>']'
<Op> ::= '+' | '*' | '^'
<Letter> ::= 'a' | 'b' | 'c' | 'd'

ここで 'a', 'b', 'c', 'd' はそれぞれ 4 桁のパスワードの先頭から 1 桁目，2 桁目，3 桁目，4 桁目の数字を表す．'+', '*', '^' は演算子であり，以下のような意味を表す．

'+' : 続く 2 つの <Hash> を二進数で表したときの論理和を取る

'*' : 続く 2 つの <Hash> を二進数で表したときの論理積を取る

'^' : 続く 2 つの <Hash> を二進数で表したときの排他的論理和を取る

ここで，論理和，論理積，排他的論理和の真理値表はそれぞれ以下のようになる．

A

B

[+AB]

0

0

0

1

0

1

0

1

1

1

1

1

A

B

[*AB]

0

0

0

1

0

0

0

1

0

1

1

1

A

B

[^AB]

0

0

0

1

0

1

0

1

1

1

1

0

例として，ハッシュ関数 [+c[+a[^bd]]] にパスワード 0404 を入力すると，ハッシュ値として 0 が得られる．同じハッシュ値が得られるパスワードとして 0000, 0101, 0202, 0303, 0505, 0606, 0707, 0808, 0909 がある．

あなたのパスワード P をハッシュ関数 S に入力した結果を出力せよ．また，ハッシュ値からパスワードを一意に特定可能なハッシュ関数の使用を防ぐため，あなたのパスワードと同じハッシュ値になるパスワードの数も出力せよ．

Input

入力は最大で 50 個のデータセットから構成される．各データセットは次の形式で表される．

S
P

各データセットの1行目は，ハッシュ関数の計算式 S である．各データセットの2行目は，4 桁の 0 から 9 の範囲の数字で構成されるパスワード P である．ハッシュ関数 S の長さは 80 以下であると仮定してよい．

入力の終了は '.' の1文字だけを含む行で表される．

Output

各データセットについて，P を S に入力して得られるハッシュ値と，P と同じハッシュ値が得られるパスワードの数を空白区切りで出力せよ．

Sample Input

[+c[+a[^bd]]]
0404
[*b[*[*cd]a]]
7777
[^[^ab][^cd]]
1295
a
9876
[^dd]
9090
.

Output for the Sample Input

0 10
7 1
15 544
9 1000
0 10000

Solution in Scala (judge verdict: Accepted)

import scala.annotation.tailrec
//https://onlinejudge.u-aizu.ac.jp/challenges/sources/JAG/Prelim/2883
object Main extends App {
  import scala.io.StdIn._
  solve
  @tailrec def solve: Unit = {
    val expresssion = readLine.trim.toList
    if (expresssion == '.'::Nil) return
    val password = new Password(readLine.trim.toInt)
    expresssion match{
      case hash Hash Nil ⇒ {
        val checkSum = hash.calHash(password)
        println(
          s"$checkSum ${(0 until 10000).count{p ⇒ hash.calHash(new Password(p)) == checkSum}}"
        )
      }
      case _ ⇒ throw new Exception
    }
    solve
  }

  case class Password(a: Int, b: Int, c: Int, d:Int) {
    def this(password: Int){
      this(password / 1000, (password / 100) % 10, (password / 10) % 10, password % 10)
    }
  }
  sealed trait HashFunc{
    def calHash(password: Password): Int
  }
  object Hash {
    def unapply(arg: List[Char]): Option[(HashFunc, List[Char])] = arg match {
      case '['::Operator(op, hashA Hash (hashB Hash ']'::t)) ⇒ Some(op.operation(hashA, hashB), t)
      case l Letter t ⇒ Some(l, t)
      case _ ⇒ None
    }
  }
  class Hash(private val func:(Password)⇒ Int) extends HashFunc {
    override def calHash(password: Password): Int = func(password)
  }
  sealed trait Letter extends HashFunc
  object Letter {
    def unapply(arg: List[Char]): Option[(Letter, List[Char])] = arg match {
      case c::t if ('a' to 'd').contains(c) ⇒ Some(new ReadLetter(c), t)
      case _ ⇒ None
    }
  }
  class ReadLetter(private val char: Char) extends Letter {
    override def calHash(password: Password): Int = char match {
      case 'a' ⇒ password.a
      case 'b' ⇒ password.b
      case 'c' ⇒ password.c
      case 'd' ⇒ password.d
      case _ ⇒ throw new Exception
    }
  }
  sealed trait Operator {
    def operation(hashA: HashFunc, hashB: HashFunc): HashFunc
  }
  object Operator {
    def unapply(arg: List[Char]): Option[(Operator, List[Char])] = arg match {
      case '+'::t ⇒ Some(OrOp, t)
      case '*'::t ⇒ Some(AndOp, t)
      case '^'::t ⇒ Some(XorOp, t)
      case _ ⇒ None
    }
  }
  object AndOp extends Operator {
    override def operation(hashA: HashFunc, hashB: HashFunc): HashFunc = new Hash({p ⇒ hashA.calHash(p) & hashB.calHash(p)})
  }
  object OrOp extends Operator {
    override def operation(hashA: HashFunc, hashB: HashFunc): HashFunc = new Hash({p ⇒ hashA.calHash(p) | hashB.calHash(p)})
  }
  object XorOp extends Operator {
    override def operation(hashA: HashFunc, hashB: HashFunc): HashFunc = new Hash({p ⇒ hashA.calHash(p) ^ hashB.calHash(p)})
  }
}