//id.java
import java.io.*;

class id {
    BufferedReader cin = null ;
    PrintStream cout = null;

  int rand( ) { return (int)(Math.random( ) * 32768.0);  }

  public static void main(String xxx[ ]) {
      new id( );
  }
  id(  ) {
    prepareIO( );
    main( ); // original main( ) in C
  }//id(

  id(BufferedReader cin, PrintStream ooo) {  //////////
    this.cin = cin;
    cout = ooo;
    main( ); // original main( ) in C
  }//id(

 void prepareIO( ) {
   try {
       cin = new BufferedReader(
              new InputStreamReader( System.in ) );
       cout = new PrintStream( System.out );
   }catch(Exception e) {  }
 } // IO ok

/*************

//idcheck.c -- by tsaiwn@csie.nctu.edu.tw
#include <stdio.h>
#include <ctype.h>
void genID( ), squeeze(char*); // 宣告兩個 function 
int testID(char*);  // 查驗身分證各種可能的錯誤 並傳回錯誤代碼
void printError(int code); // print error message according code
// Java 不需要宣告, 因為在 class 內沒有先後關係 
//寫個程式可以檢查身分證號碼是否正確
//Extra credit: 輸入 999 則產生一個可用的合法身分證號碼
*************************************************************/

int main( ) {
   String id=null; // static char id[99];  // 夠'長吧 :-) 身分證才 10 碼啦 
   int code = 0;   // 用來記住錯誤代碼 
   while(38 == 38) {   // for( ;; ) {
       cout.printf("請輸入身分證號碼 Input ID: ");
       try{
          id = cin.readLine( ) ; //fgets(id, sizeof(id), stdin); // 整列讀入 到 id
       }catch(Exception e) { e.printStackTrace( ); }
       if(id==null) break; //if(feof(stdin)) break;

       id = id.replace(" ", ""); //squeeze(id); // 把所有空白都去掉  white space

       if(id.equals("-1") ) break;  //if(strcmp(id, "-1") == 0) break; 
       if(id.equals("quit") ) break;   //if(strcmp(id, "quit") == 0) break; 
       if(id.toUpperCase( ).equals("quit".toUpperCase( )) ) break;
       if(id.equals("999") ) {genID( ); continue; }
              // if(strcmp(id, "999") == 0) {genID( ); continue; }
       code = testID(id);  // 取得錯誤代號, 0 表示無錯 :-)
       cout.printf(" ID %s is ", id);
       if(code==0) cout.printf(" OK.\n");   // 印出說這號碼正確 
       else {
          cout.printf(" Error! Reason(s):\n");
          printError(code); // 依據 code 印訊息 
       }//if
   }// while(
   cout.printf("\r\nThank you and bye bye!\n");
   return 0;
} // main(

/****** Java 沒用 !!!
//關於  squeeze(char*) 這好用的 function, 因為 C 程式庫沒有,自己寫:
void squeeze(char*p) {    // 擠掉所有的 white space
   char*p2 = p;    
   if(*p == 0)return;
   while(*p2 !=0) {  // white space 請看  K&R課本第二章與附錄 B
      if(isspace(*p2)) { ++p2; continue; }  // 丟掉 white space
      *p = *p2; 
      p++; ++p2;   // advance one char
   }//while
   *p = *p2;  // 0 == '\0'   == NULL 
}// squeeze(
******************************************/

// 關於 int testID(char* id) : 依據身分證規則查看 id 傳回錯誤代碼
int yy[ ]={ 10,11,12,13,14,15,16,17, 34,  //ABCDEFGH I
            18,19,20,21,22,  35,   //JKLMN O
            23,24,25,26,27,28,     //PQRSTU
            29,32,30,31, 33 };     //VWXY Z 
/*****
int checkSum(char * id){  // 幫忙算 checkSum 給 testID(id) 用 
*******/

int checkSum(String idid){
   String id = idid;
   while(id.length() < 10) id = id+"0";   // 防呆
   int sum, i;    // 因編碼沒完全照字母順序, 用算的要很多 if(...
   int ynum;
   // 用查表法 table look up 查出字母對應的兩位數較簡單直覺 !
   // 先建個表 int yy[ ] = { 10, 11, 12, 13, ...};  // 照規定 AB..
   // 然後 Let i = id[0]字母減去 'A' 得到 0..25
   // 再查出 yy[i] 拿來用: ynum = yy[i];  // 10..O是35..Z不是35 !!
   i = Character.toUpperCase( id.charAt(0) ) - 'A';
   ynum = yy[i];
   sum = ynum/10 + 9* (ynum%10);  // weight 1, 9, [876543211]
   for(i=1; i<=8; ++i) sum += (id.charAt(i) - '0') *(9-i);  // 87654321
   sum += (id.charAt(9) - '0') ;  // *1   檢查碼 weight 也是  1
   return sum;   // 我只負責算出 checksum
}//checkSum(

/******
int testID(char* id) {   //傳回錯誤代碼, 可用 bitwise "&" 運算找出 
********/

int testID(String idid) {  
   String id = idid;
   if(id.length( ) < 1) id = "?";   // 防呆
   if(id.length( ) < 2) id = id+"?";  // 防呆

   int i, ans = 0, sum=0;  // sum 用來算 weighted check sum
   char id00 = Character.toUpperCase(id.charAt(0)); // 轉為大寫
   if(!Character.isLetter(id00)) ans = ans + 1;  // 1 號錯  ans = ans | 1;
   if(id.charAt(1) != '1'  && id.charAt(1) != '2') ans += 2; // 2 號錯 男生女生?
   if(id.length( ) < 10) ans += 4;   // 太短
   if(id.length( )  > 10) ans += 8;   // 太長
   for(i=1; i<= id.length( )-1; ++i) if(!Character.isDigit(id.charAt(i))) ans = ans | 16; // 非數字
   if((ans&16) != 0) return ans;  // 有非數字不用再算 check sum 啦 
   if(ans != 0) return ans;  // 有任何錯就..就不用再算 check sum 啦 
   sum = checkSum(id);   // 假設沒有其他怪字就算出 check sum
   if(sum%10 != 0) ans |= 32;   // 必須除以 10 除得盡才對 
   return ans;
}// testID(

String what[ ]={ "對啦!!這是合法的身份證字號", //訊息0
                "ㄟ..第一個字非字母",    // 訊息1
                "你是第三性嗎?",         // 訊息2
              "太短了!不足碼唷!!",   // 訊息3
              "怎麼會有這麼多碼!!",  // 訊息4
            "打錯啦!!應該是數字喔!!",   // 訊息5     
            "神秘數字算出來是錯的??"     // 訊息6
                };
// String what[ ] = { ....};
void printError(int code) {
   int i, yy[ ] = {0, 1, 2, 4, 8, 16, 32, 64, 128};
   if(code == 0) { cout.printf("%s\n", what[0]); return; }
   for(i=1; i <= 6; ++i)if((code&yy[i])!=0) cout.printf("\r%s\n", what[i]);
}// printError(
void genID( ) {
   String id = "";   //char id[11]={ 0 };
   int i;
   id  = id +  (char)( 'A' +  rand( ) % 26) ;  // 'A' .. 'Z'
   id  +=  (char)( '1' + rand( ) % 2 );  // '1' .. '2'
   for(i=2; i<=8; ++i) {
      id +=  (char)( '0' + rand( )%10 ); // 用亂數生出 id[2] .. id[8]
   } //
   /// id[9] 是檢查碼, 要算, 可先塞 '0' 偷叫 checkSum( )再調整 
   int sum = checkSum( id+"0");
   sum = 10 - (sum%10);
   if(sum == 10) sum = 0;
   id = id + sum;  // sum 會自己變成字串 
   cout.printf(" Good ID: %s\n", id);
}// genID(

}//class
