EclEmma

EclEmma는 자바에서 테스트 커버리지를 측정해 주는 이클립스 플러그인 입니다. 설치 방법 및 사용 방법은 아주 간단하지만 한번 보도록 하죠.

EclEmma 설치

  1. 이클립스 상에서 help -> Eclipse MarketPlace...를 누른 후

    EclEmma

    Install 버튼을 눌러서 설치 해 줍니다.


실행

  • 초/빨 색의 막대바가 있는 실행 버튼이 생깁니다.

    EclEmma_bar

  • 그러면 아래와 같이 프로젝트, 패키지, 클래스에 대한 결과를 볼 수 있습니다.

    EclEmma_trello

    녹색 : 테스트로 검증 된 분분

    빨간색 : 테스트가 필요한 부분

    노란색 : 부분적으로 테스트 된 곳


여담

사실 도메인 단에서 커버리지가 많이 떨어지길래 모델에 대한 유닛 테스트는 꼼꼼히 작성 했다고 생각했던지라 나름 충격을 받아서 보니 이클립스를 통해 Generate hashcode() and equals()로 만든 메서드 부분이 문제였네요…

EclEmma_member

경우에 따라선 똑같이 자동으로 만든 equals() 메서드가 초록, 노랑, 빨간이 알록달록하게 뜨는 경우를 보게 되기도 합니다.

특히 로직이 별로 없는 모델이 있을 경우 커버리지가 확 떨어지는 결과가 나오는 군요. 커버리지를 올리기 위해 테스트 코드를 작성하면 해결 될 일이긴 하지만, 정말 hashcode(), equals()에 대한 테스트 코드까지 매번 만들어 줘야 할지는 단순히 숫자에 대한 강박으로 커버리지를 올리기 위한 작업은 아닌가 싶은 생각이 들어서 한번 생각 해 봐야 할 것 같네요.

압축 (compression) – 4

인프런의 영리한 프로그래밍을 위한 알고리즘 강좌를 보고 작성한 문서입니다.


Codeword 부여하기

huffman_codeword1

  • 여기서 prefix를 하나의 32비트 정수로 표현한다. 하지만 32비트 중에서 하위 몇 비트만이 실제 부여된 codeword이다. 따라서 codeword의 길이를 유지해야 한다.

class Run 수정하기

class Run implements Comparable<Run> {
  public byte symbol;
  public int runLen;
  public int freq;

  // 트르의 노드로 사용하기 위해서 왼쪽 자식과 오른쪽 자식 노드 필드를 추가한다.

  // 노드에 부여된 codeword를 지정하기 위한 필드들을 다음과 같이 추가한다.

  public int codeword;    //부여된 codeword를 32비트 정수로 저장
  public int codewordLen; // 부여된 codeword의 길이. 즉 codeword의
                          // 하위 codewordLen비트가 실제 codeword
}

Java에서 비트(bit) 연산

public class Test {
  public static void main(String args[]){
    int a = 60;   // 60 = 0011 1100
    int b = 13;   // 13 = 0000 1101
    int c =0;

    c = a & b;        // 12 = 0000 1100
    System.out.println("a & b = " + c);
    c = a | b;        // 61 = 0011 1101
    System.out.println("a | b = " + c);
    c = a ^ b;        // 49 = 0011 0001
    System.out.println("a ^ b = " + c);
    c = ~a;           // -61 = 1100 0011
    System.out.println("~a = " + c);
    c = a << 1;       // 120 = 0111 1000
    System.out.println("a << 1 = " + c);
    c = (a << 1) + 1; // 121 = 0111 1001
    System.out.println("(a << 1) + 1 = " + c);
    c = a << 2;       // 240 = 1111 0000
    System.out.println("a << 2 = " + c);
  }
}

codeword 부여하기

huffman_codeword2


main과 compressFile 메서드

public class HuffmanCoding {
  ...

  public void compressFile(RandomAccessFile fIn) {
    collectRuns(fIn);
    createHuffmanTree();
    assignCodewords(theRoot, 0, 0);
  }

  static public void main(String args[]){
    HuffmanCoding app = new HuffmanCoding();
    RandomAccessFile fIn;
    try{
      fIn = new RandomAccessFile("sample.txt","r");
      app.compressFile(fIn);
      fIn.close();
    } catch(IOException io){
      System.err.println("Cannot open " + fileName);
    }
  }
}
압축 (compression) – 3

인프런의 영리한 프로그래밍을 위한 알고리즘 강좌를 보고 작성한 문서입니다.


Huffman Tree

  • Huffman coding 알고리즘은 트리들의 집합을 유지하면서

최소 힙

huffman_tree1

huffman_tree2

huffman_tree3

huffman_tree4

huffman_tree5


class run 수정하기

class Run implements Comparable<Run> {
  public byte symbol;
  public int runLen;
  public int freq;
  // 트리의 노드로 사용하기 위해서 왼쪽 자식과 오른쪽 자식 노드 필드를 추가한다.

  // 두 run의 크기관계를 비교하는 compareTo 메서드를 overriding하라.
  // 비교의 기준은 freq이다.
}

class Heap

  • 기존에 작성했던 Heap클래스를 가져와서 사용한다. Generics로 수정하고, heapify, insert, extractMin등의 함수들을 min heap에 맞게 수정한다.

  • C로 구현하는 사람들은 별개의 모듈로 min heap을 구현하라.


Huffman Tree 만들기

public class HuffmanCoding {
  private ArrayList<Run> runs = new ArrayList<Run>();

  private Heap<Run> heap; // minimum heap이다.
  private Run theRoot = null; // root of the Huffman tree

  private void createHuffmanTree() {
    heap = new Heap<Run>();

    // 1. store all runs into the heap.
    // 2. while the heap size > 1 do
    //    (1) perform extractMin two times
    //    (2) make a combined Tree
    //    (3) insert the combined tree into the heap.
    // 3. Let the Root be the root of the tree.
  }
}

Huffman Tree 출력해보기

private void printHuffmanTree() {
  preOrderTraverse(threRoot, 0);
}

private void preOrderTraverse(Run node, int depth) {
  for (int i=0; i<depth; i++)
    System.out.print("  ");
  if (node == null){
    System.out.println("null");
  } else {
    System.out.println(node.toString());
    preOrderTraverse(node.left, depth + 1);
    preOrderTraverse(node.right, depth + 1);
  }
}
  • class Run에 적절한 toString 메서드를 추가하여 트리를 출력해보자.
압축 (compression) – 2

인프런의 영리한 프로그래밍을 위한 알고리즘 강좌를 보고 작성한 문서입니다.


Huffman Method with Run-Length Encoding

압축의 종류 :

  • 파일을 구성하는 각각의 run들을 하나의 super-symbol로 본다. 이 super-symbol들에 대해서 Huffman coding을 적용한다.

  • 예를 들어 문자열 AAABAACCAABA은 5개의 super-symbol들 AAA, B, AA, CC 그리고 A로 구성되며, 각 super-symbol의 등장횟수는 다음과 같다.

    huffman_method1

    huffman_method2


제1단계 : Run과 frequency 찾기

  • 압축할 파일을 처음부터 끝까지 읽어서 파일을 구성하는 run들과 각 run들의 등장횟수를 구한다.

  • 먼저 각 run들을 표현할 하나의 클래스 class Run을 정의한다. 클래스 run은 적어도 세 개의 데이터 멤버 symbol, runLen, 그리고 freq를 가져야 한다. 여기서 symbol은 byte타입이고, 나머지는 정수들이다.

  • 인식한 run들은 하나의 ArrayList에 저장한다.

  • 적절한 생성자와 equals 메서드를 구현한다.

  • 데이터 파일을 적어도 두 번 읽어야 한다. 한 번은 run들을 찾기 위해서, 그리고 다음은 실제로 압축을 수행하기 위해서.

  • 여기서는 RandomAccessFile을 이용하여 데이터 파일을 읽어본다.

// 읽을 데이터 파일을 연다
RandomAccessFile fIn = new RandomAccessFile(fileName, "r");

// 한 byte를 읽어 온다. 읽어온 byte는 0~255사이의 정수로 반환된다.
// 파일의 끝에 도달하면 -1을 반환한다.
int ch = fIn.read();

// 필요하다면 byte로 casting해서 저장한다.
byte symbol = (byte)ch;

Run 인식하기

run1


Run과 frequency 찾기

JAVA

public class HuffmanCoding {
  // 인식한 run들을 저장할 ArrayList를 만든다.
  private ArrayList<Run> runs = new ArrayList<Run>();

  private void collectRuns(RandomAccessFile fIn) throws IOException {
    // 데이터 파일 fIn에 등장하는 모든 run들과 각각의 등장횟수를 count하여
    // ArrayList runs에 저장한다.
  }

  static public void main(String args[]){
    HuffmanCoding app = new HuffmanCoding();
    RandomAccessFile fIn;
    try {
      fIn = new RandomAccessFile("sample.txt","r");
      app.collectRuns(fIn);
      fIn.close();
    } catch (IOException io) {
      System.err.println("Cannot open " + fileName);
    }
  }
}

C

typedef struct run Run;
typedef struct run {
  unsigned char symbol;
  int run_length;
  int freq;
};

Run *runs[MAX];
int number_runs = 0;

void collectRuns(FILE *fIn){
  // 데이터 파일 fIn에 등장하는 모든 run들과 각각의 등장횟수를 count하여
  // 배열 runs에 저장한다.
  // Run 객체들을 동적메모리할당으로 생성하여 배열 runs에서 객체의 주소를 저장하고
  // 배열의 크기가 부족할 경우 array doubling을 하도록 구현하라.
}
압축 (compression) – 1

인프런의 영리한 프로그래밍을 위한 알고리즘 강좌를 보고 작성한 문서입니다.


Case Study : Huffman Coding

압축의 종류 :

  • 무손실(lossless) : 손실 되면 안되는 정보들을 담는 경우. 텍스트 등등

  • 손실(lossy) : 손실 되더라도 사람이 인지 못할 정도면 문제 없는 경우. 동영상…


Huffman Coding

  • 가령 6개의 문자 a, b, c, d, e, f로 이루어진 파일이 있다고 하자. 문자의 총 개수는 100,000개이고 각 문자의 등장 횟수는 다음과 같다.

    huffman1

  • 고정길이 코드를 사용하면 각각의 문자를 표현하기 위해서 3비트가 필요하며, 따라서 파일의 길이는 300,000비트가 된다.

  • 위 테이블의 가변길이 코드를 사용하면 224,000비트가 된다.


Prefix Code

  • 어떤 codeword도 다른 codeword의 prefix가 되지 않는 코드(여기서 codeword란 하나의 문자에 부여된 이진코드를 말함)

  • 모호함이 없이 decode가 가능함

  • prefix code는 하나의 이진트리로 표현 가능함

    prefix_code


Huffman Coding

huffman2

huffman3

왼쪽과 오른쪽 둘 다 압축률은 같다.

huffman4


Run-Length Encoding

  • 런(run)은 동일한 문자가 하나 혹은 그 이상 연속해서 나오는 것을 의미한다. 예를 들어 스트링 s = “aaabba”는 다음과 같은 3개의 run으로 구성된다: “aaa”, “bb”, “a”.

  • run-length encoding에서는 각각의 run을 그 “run을 구성하는 문자”와 “run의 길이”의 순서쌍 (n, ch)로 encoding한다. 여기서 ch가 문자이고 n은 길이이다. 가령 위의 문자열 s는 다음과 같이 코딩된다: 3a2b1a.

  • Run-length encoding은 길이가 긴 run들이 많은 경우에 효과적이다. 아닌 경우 압축의 효과가 적음, 이미지나 멀티미디어 데이터의 경우 효율적(유사 픽셀이 연속적으로 나옴)

  • 무손실 압축이다.