koodev

Emacs Wdired 모드

Emacs

Wdire 모드에서 (query-replace) 를 실행한 모습

이번주에 폴더 안의 파일들의 이름에 접두어를 붙여서 바꿀일이 있었다. Python 스크립트를 만들어서 실행시켰는데, 잠시 후 이를 취소해야겠다는 생각이 들었다. 그래서 다시 취소하는 Python 스크립트를 만들어야 하나 했었는데, Emacs의 Wdired 모드를 사용하여 간단하게 파일 이름들을 되돌려 놓을 수 있었다.

Wdired 모드는 Dired 버퍼에서 동작하는 모드이다. 원래 Dired 버퍼는 읽기 전용인데, Wdired 모드는 이를 "부분" 쓰기허용으로 만든다. 즉, 여기서 W는 "writable"을 의미한다.

Wdired 모드를 활성화 시키려면 우선 Dired 모드로 진입하고 나서, C-x C-q (dired-goggle-read-only) 를 누르면 된다.

Wdired 모드에 진입하면 파일 이름을 수정할 수 있다. 커서로 수정하는 것 뿐만 아니라 (replace-regexp) 같은 것으로도 편집이 가능하다. 파일 이름을 몽땅 지우면 해당 파일이 삭제되니 주의하자. 파일 이름만 수정 가능하고 다른 부분(권한 등)은 기본적으로 편집할 수 없다. 그런데 wdired-allow-to-change-permissions 변수를 t 로 바꾸면 권한 수정을 할 수 있게 된다.

수정된 내용은 바로 반영되는 것은 아니고 C-c C-c 이나 파일 저장할 때 쓰는 C-x C-s 를 눌러야 반영된다. Wdired 모드에서는 둘 다 (wdired-finish-edit) 로 매핑되어 있다. 취소하기 위해서는 C-c C-k (wdired-abort-finish) 를 하면 된다.

참고

'Emacs' 카테고리의 다른 글

Emacs Major/Minor 모드  (0) 2019.10.23
Emacs - Python 문서 열람시 멈춤현상  (0) 2019.09.18
macOS 에서 Emacs 데몬이 종료되지 않는 문제  (0) 2019.06.07
Create a new file in ido mode  (0) 2019.06.07
Emacs Server setting  (0) 2019.05.15

macOS 에서 Emacs 데몬이 종료되지 않는 문제

Emacs

macOS 에서 Emacs Server/Client 환경을 만들던 중 데몬이 종료가 되지 않는 문제에 대해서 정리했다.

문제점

Emacs Server/Client 설정은 EmacsWikiEmacs As Daemon 에서 잘 설명해주고 있다. Ubuntu 에서의 설정은 Emacs As Daemon 을 따라서 성공했다. 그런데 macOS에서는 Emacs 서버 데몬을 실행한 후 시스템종료가 안 되는 현상이 발생했다.

Ubuntu 에서는 Emacs 데몬의 systemd 설정파일에서 시스템 종료 시점에 emacsclient --eval "(kill-emacs)" 을 날려주도록 설정해 놨는데, macOS의 시작프로그램 관리자인 launchd 에서는 종료시점에 대해 뭔가를 할 수가 없게 되어있다. 따라서 Emacs 데몬이 종료를 못하고 강제종료를 해 주어야지만 시스템을 종료할 수가 있는 것이다. 내가 직접 테스트해보지는 않았지만 이 문제는 Emacs 를 데몬이 아닌 모드로 실행 후에 (server-start) 를 직접 또는 init.el 등에서 실행해 주면 괜찮다고 한다.

아래 글을 쓴 redinger 라는 분도 macOS에서의 Emacs 데몬 시작 스크립트를 공유해 두시고 나와 같은 문제로 인해서 사용을 하고 있지 않다고 한다.

https://gist.github.com/redinger/1394910/c0dc6877c158a13760212dbe10b4abb32ae3c4f8

그런데 (server-start) 를 불러주는 방식은 최초에 Emacs 실행 시 데몬 모드가 아닌 일반 모드로 실행을 해야 하는 번거로움이 있어서 개인적으로 마음에 들지 않는 방식이다.

데몬 종료가 되지 않는 원인

데몬 종료가 안 되어서 시스템 종료를 방해하는 원인은 바로 Emacs 개인 설정에서 Emacs 종료시에 편집 중이던 수정된 파일을 저장할지 안할지 확인하는 과정이 들어가 있기 때문이었다. Emacs를 데몬으로 실행해 버리면 이를 확인할 수가 없기 때문이다. 즉, 아래와 같은 설정이 문제였다.

(setq confirm-kill-emacs 'yes-or-no-p)

해결책

그런데 Emacs를 일반 모드로 실행하는 경우도 있기 때문에 위의 설정을 포기하는 것도 많이 아쉬웠다. 방법은 Emacs가 데몬 모드로 실행될 경우에는 confirm 설정을 포기하고, 그렇지 않은 일반 모드일 경우에만 confirm 설정을 사용하도록 init 파일에서 설정하면 되는 것이다. Emacs에는 daemonp 라는 함수가 데몬모드로 실행 중인 경우 t 를 리턴하게 되어 있다. 즉, 아래와 같은 내용을 init 파일에 넣어주면 된다.

(if (daemonp)
    nil
  (setq confirm-kill-emacs 'yes-or-no-p))

References

'Emacs' 카테고리의 다른 글

Emacs - Python 문서 열람시 멈춤현상  (0) 2019.09.18
Emacs Wdired 모드  (0) 2019.06.29
Create a new file in ido mode  (0) 2019.06.07
Emacs Server setting  (0) 2019.05.15
EIN and virtualenvwrapper  (0) 2018.07.22

미분 관련 수학 기호

Math

편미분 기호 -  ∂

편미분 기호. 읽을 때는 [del] 이라고 읽는다(그동안 a 나 6이라고 읽었다...). 편미분을 영어로는 'partial derivative' 라고 하며, HTML 코드는 ∂, LaTex 코드는 \partial 이다.

위의 수식을 영어로 읽을 때는 "the partial derivative of z with respect to x" 와 같이 읽으면 된다.

그라디언트 - ∇

다차원 공간에서 편미분한 것들을 모아 놓은 것을 그라디언트 라고 한다. 읽을 때는 [nabla] 라고 읽고(이제 세모 또는 역삼각형 이라고 읽지 말자...), HTML 코드는 ∇, LaTex 코드는 \nabla 이다.

참고로 'nabla'는 고대 그리스 페키니아의 현악기의 이름에서 가져왔다고 한다.

한글로는 정확하지 않지만, 그냥 '기울기' 라고 하는 것 같다.

'Math' 카테고리의 다른 글

미분 Chain Rule  (0) 2019.07.31
미분 곱의 법칙 (Product rule)  (0) 2019.07.22
Right Coset  (0) 2017.06.27
Lagrange's Theorem과 Counting Formular  (0) 2017.06.22
Subgroup으로 생성되는 Left Coset  (0) 2017.06.16

Create a new file in ido mode

Emacs

예전에 다른 곳에 썼던 글을 가져온다. 글이 쓰여진 시점은 2017년 1월 이다.

Emacs를 사용하기 시작한지 4년 가까이 되었지만 최근에서야 ido mode를 사용하려고 하고있다. 익숙해지려고 하던 도중 파일 생성에 대해 문제점이 있어서 관련 내용을 정리해 본다.

상황은 다음과 같다. 파일을 생성하려고 C-x C-f 를 누르고 해당 파일 이름을 입력했다. ido-create-new-buffer 변수가 ‘alway로 잡혀 있기 때문에 매칭되는 파일이 없으면 자동으로 새 버퍼를 연다. 그런데 ido mode가 이전에 다루었던 path들을 캐시하고 있기 때문에 이전 path에 매칭되는 파일이 있을 경우 새 버퍼를 여는 대신에 해당 파일의 이름으로 버퍼를 열게된다. 파일 생성을 하고 싶은데 이전 경로의 파일을 열게 되는 것이다. 이런 상황이 많지는 않을 것 같기도 하지만 소스코드에 따라서 Makefile이나 CMakeList.txt 같은 파일들은 디렉토리마다 존재할 수가 있고, 새로 만드려고 할 경우 문제가 될 수도 있다.

ido 모드를 빠져나가기

C-x C-f C-f 이걸 누르면 ido 모드를 종료하고 일반적인 find file 모드로 진입하게 된다. 그리고 생성할 파일 이름을 입력하면 (지나치게) 스마트하게 이전 path를 뒤져서 파일을 열지 않고 입력한 해당 경로의 버퍼를 열게 된다. 가장 적당한 방법이라고 생각한다.

C-j

또 다른 방법은 ido-find-file 에서 생성할 파일 이름을 입력하고 재빨리 C-j 를 누르면 해당 파일이 생성된다. 하지만 너무 늦을 경우 이전 path를 뒤지게 되니 바짝 서둘러서 진행해야 한다.

ido-auto-merge-work-directories-length

ido-auto-merge-work-directories-length 변수를 -1로 설정할 경우 이전 path를 뒤지는 짓을 하지 않도록 막는다. 이전 path를 뒤지는 기능이 아쉽겠지만 작업환경에 따라서는 유용할 수도 있을 것 같다.

References

'Emacs' 카테고리의 다른 글

Emacs - Python 문서 열람시 멈춤현상  (0) 2019.09.18
Emacs Wdired 모드  (0) 2019.06.29
macOS 에서 Emacs 데몬이 종료되지 않는 문제  (0) 2019.06.07
Emacs Server setting  (0) 2019.05.15
EIN and virtualenvwrapper  (0) 2018.07.22

Tentative Symbol in C

Programming

예전에 다른 곳에 썼던 글을 그대로 가져온다. 본문에서 '요즘'은 2016년 11월 기준이다.

요즘 링커와 로더와 관련된 글을 읽고있다. 읽어본 글들 중 Ali Bahrami 라는 분의 글에 나온 tentative symbol 에 대한 내용을 정리해본다. 예제는 Ali 님의 글에서 그대로 가져왔고 내 맥북에서 실행(Apple llvm cc)해본 결과를 붙인다. tentative 심볼은 결코 좋은 코딩 스킬은 아니지만(goto 같은 존재?) C언어나 링커의 내부 동작을 참고하는데 도움이 되는 내용인 것 같아 정리해둔다.

“Tentative” 를 네이버 영어사전에서 찾아보면 “처리, 합의 등이 잠정적인” 이런 뜻의 형용사로 나와 있다. 즉, tentative 심볼은 (뭔가 부족한) 임시 심볼이라고 할 수 있다.

ELF 심볼 테이블의 엔트리에는 심볼 타입에 대한 속성이 있다. 가능한 속성값들 중 STT_OBJECT 는 일반 데이터에 대한 정의를 나타내고, STT_COMMON 은 tentative 데이터에 대한 정의를 나타낸다. 대부분의 변수 심볼은 STT_OBJECT 로 잡힐 것인데, 그럼 STT_COMMON 언제 잡히며, 왜 COMMON 하지도 않으면서 COMMON 이란게 붙었을까?

tentative 심볼은 글로벌 변수인데 크기나 초기값을 알 수 없는 변수를 추적할 때 쓰인다. 이런 심볼은 해당 오브젝트의 컴파일 시점에서는 저장될 위치가 결정되어 있지를 않다. 이런 심볼을 “common block” 이라고도 부른다. 이렇게 부르는 이유는 포트란의 COMMON block 에서 유래되었기 때문이다.

아래 C코드의 두 선언문을 살펴보자.

int foo;
int foo = 0;

링커와 로더나 Embedded Recipe 같은 책에 의하면 초기화되지 않은 변수는 BSS 영역으로 잡힌다. 따라서 둘 다 모두 초기값이 0인 foo라는 정수형 변수의 선언문일 것이다… 라고 생각하면 안된다! 초기화되지 않은 변수가 BSS로 할당되는 것은 맞지만 첫 번째 선언문만 봐서는 foo가 초기화 되었는지 안되었는지 알 수 없다. 즉, 다른 파일에서 초기화를 해버렸을 수도 있는데, 이 파일만 봐서는 그걸 알 수가 없는 것이다. 따라서 첫 번째 선언문의 foo값은 어떤 파일이 링크되느냐에 따라 달라지게 된다.

아래의 예제 t1.c 와 t2.c 를 살펴보자.

#include <stdio.h>

#ifdef TENTATIVE_FOO
int foo;
#else
int foo = 0;
#endif

int main(int argc, char *argv[])
{
  printf("FOO: %d\n", foo);
  return 0;
}
int foo = 12;

처음에는 t1.c 만 빌드해보자. TENTATIVE 매크로도 확인해보자.

$ cc -DTENTATIVE_FOO t1.c
$ ./a.out
FOO: 0
$ cc t1.c
$ ./a.out
FOO: 0

매크로를 주던 안주던 간에 foo 변수값은 0으로 확인된다. 자 이제 t2.c 를 링크해보자. 이번엔 결과가 좀 다르다.

$ cc -DTENTATIVE_FOO t1.c t2.c
$ ./a.out
FOO: 12
$ cc t1.c t2.c
duplicate symbol _foo in:
    /var/folders/0l/1xtwrc4j2978f6rhsk7jq4y80000gn/T/t1-1ba0e4.o
    /var/folders/0l/1xtwrc4j2978f6rhsk7jq4y80000gn/T/t2-3061bc.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

매크로를 줬을 경우, 즉, foo를 선언만 해놓고 초기화를 하지 않았을 경우 0으로 초기화가 되는 것이 아니라 t2.c 에서 초기화한 값을 그대로 가져온다. 그리고 매크로를 주지 않았을 경우, 링커(ld)에서 두 개의 foo 에 대한 non-tentative 하며 다른 값을 가진 정의에 대해 에러를 뱉어낸다.

C 문법에서는 명시적인 값을 지정하지 않은 변수의 초기값은 0이 할당된다고 되어 있다. 그러나 똑같은 이름의 글로벌 변수가 존재할경우 이것을 바꿀 수 있다. C 컴파일러는 컴파일중에는 파일 하나만 보기 때문에 이런 내용에 대해서 알 수가 없다. 따라서 컴파일러는 초기화되지 않은 변수를 만나게 되면 해당 심볼의 타입을 STT_COMMON 으로 해 두고 나중에 링커가 리졸브(resolve)하도록 내버려둔다. 링커는 파일 하나만 보는 것이 아니기 때문에 이런 심볼 리졸브가 가능하다. 그렇지만 링커가 프로그래머의 의도까지 잘 파악하는 것은 아니기 때문에 이렇게 초기화되지 않은 변수를 쓸 경우 동작이야 잘 하겠지만 그게 의도에 부합하는 올바른 동작일지는 명확하지 않을 것이다.

매크로를 주지 않았을 경우, 즉 변수 선언과 초기화를 했을 경우에는 t1과 t2 오브젝트 둘다 non-tentative 한 심볼이 생성된다(STT_OBJECT). 이 경우 링커에 따라 다른데 똑같이 에러를 내뱉는 경우도 있고(Apple llvm ld), Ali의 글에서는(Solaris ld) 선언문이 일치하는지를 검사한다고 한다. 둘 다 tentative 인 경우는 어떨까? 맥북에서는 이 경우 그냥 넘어갔다(0으로 초기화됨). 하지만 링커가 어떻든 간에 초기화를 하는 것이 견고하고 호환성좋은 코드인 것이다.

로컬 스코프에서 tentative symbol은 의미가 없다. 즉, 아래와 같이 static을 쓸 경우 t1.c에서의 참조는 로컬 스코프의 정의를 따라가기 때문에 foo는 tentaive 심볼이 아니다.

#ifdef TENTATIVE_FOO
int foo;
#else
static int foo = 0;
#endif

tentative 심볼을 쓰는것은 결코 좋은 코딩 스킬은 아니다. 어떤 파일에서의 변수선언이 다른 파일의 것을 건드릴 수 있기 때문이다. tentative 심볼은 포트란에서 나왔다. 포트란에서는 common block 이란 것을 사용하여 C의 union 같은 것을 만들 수 있다고 한다. 즉, 컴파일 타임에 지정된 데이터 타입이 아니라 그때그때 크기와 타입같은것들을 바꾸어 쓰기 위해 common block 이란 것을 사용했다.

글로벌 변수를 사용하는 것이 아주 좋은 디자인은 아니지만 가끔 글로벌이 필요할 때가 있다. 이 경우 선언과 초기화를 하나의 파일(오브젝트)에서만 해야 하고 이를 참조하는 레퍼런스는 초기화를 하면 안된다. C에서는 extern 이란 키워드를 지원하여, ‘이 심볼은 참조하는 것이다’ 라고 컴파일러에게 알려준다. Apple llvm cc는 extern 을 사용하고 초기화를 하면 워닝(warning)을 내뿜는다.

tentative 심볼은 extern이 붙은 심볼과는 또 다른데, extern의 경우 외부의 정의를 참조한다는 것을 명시하는 것인 반면, tentative 변수는 경우에 따라서 자기 자신이 0으로 초기화될 수 있다. 따라서 extern 글로벌 변수를 선언하고 아무데도 초기화를 안 해 놓으면 링커가 에러를 내뿜는다.

References

'Programming' 카테고리의 다른 글

Places365-Challenge mean pixel value  (0) 2019.11.07
PyTorch torch_shm_manager Runtime Error  (0) 2019.10.23
pip 설치 중 setuptools 관련 오류  (0) 2019.06.04
ARM A32 명령어셋 VZIP  (0) 2019.03.11
ARM A64 명령어셋 ZIP1, ZIP2  (0) 2019.03.11

pip 설치 중 setuptools 관련 오류

Programming

tensorflow-datasets 이란 파이썬 패키지를 설치 중 아래와 같은 오류 메시지가 나왔다.

원인은 setuptools 가 만료되서 라고 한다. pip install --upgrade setuptools

pip install --upgrade setuptools

입력 후 다시 pip install tensorflow-datasets 하니까 잘 동작하는 것을 확인했다.

참고: https://steemit.com/kr-dev/@wanabe/6hhg8

'Programming' 카테고리의 다른 글

PyTorch torch_shm_manager Runtime Error  (0) 2019.10.23
Tentative Symbol in C  (0) 2019.06.07
ARM A32 명령어셋 VZIP  (0) 2019.03.11
ARM A64 명령어셋 ZIP1, ZIP2  (0) 2019.03.11
ARM A64 명령어세트 기본적인 내용 정리  (0) 2019.03.11

Blender Wavefront(obj) 모델의 정점 인덱스 출력하기

카테고리 없음

3D 모델의 정점 인덱스를 알아야 할 경우가 종종 있다. 그런 툴을 만들어 볼까도 생각했었는데, Blender의 디버그 모드에서 그런 기능을 제공해주고 있었다.

우선 Blender를 디버그 모드로 실행해야 한다. 커맨드 라인에서 -d 옵션을 주어 실행하면 디버그 모드로 실행된다. 예를 들어 macOS에서 아래와 같이 실행하면 된다.

/Applications/Blender/blender.app/Contents/MacOS/blender -d

그리고 원하는 모델을 임포트 하는데, 임포트 옵션 중에 'Keep Vertex Order' 라는 항목이 있다. 이를 반드시 체크해 준다. 그렇지 않으면 Blender 내부에서 원본 인덱스를 수정하게 된다.

모델을 로드한 다음 그 모델을 마우스로 선택한 다음 'TAB'을 누르거나 해서 Edit Mode 상태로 바꾼다.

그리고 'N' 키를 누르면 오른쪽에 뭐라고 하는지 잘 모르겠지만 선택된 모델에 대한 여러가지 정보같은게 나오는 창이 뜬다. 이 창의 좀 아랫부분에 보면 Mesh Display 그룹에 Edge Info 라는 항목이 있다. 여기에 Indices 체크박스를 체크하면 정점 인덱스 정보가 화면에 표시된다.

Emacs Server setting

Emacs

Emacs를 서버-클라이언트 서비스로 구성하는 과정에 대해 기록한다.

환경은 Ubuntu 18.04, Emacs 26.2 이다.

참고

Starting Emacs Deamon with systemd

Systemd 는 대부분의 리눅스 배포판에서 시작 프로그램을 관리하는 프로그램이다. 아래 emacs.service 파일은 emacs26.2 소스(emacs-26.2/etc/emacs.service)에 들어있는 것을 내 환경에 알맞게 수정한 것이다. 아래와 같이 수정하여~/.config/systemd/user/emacs.service 를 만들어 준다.

## If your Emacs is installed in a non-standard location, you may need
## to copy this file to a standard directory, eg ~/.config/systemd/user/ .
## If you install this file by hand, change the "Exec" lines below
## to use absolute file names for the executables.
[Unit]
Description=Emacs text editor
Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/

[Service]
Type=simple
ExecStart=/usr/local/bin/emacs --fg-daemon
ExecStop=/usr/local/bin/emacsclient --eval "(kill-emacs)"
Environment=SSH_AUTH_SOCK=%t/keyring/ssh
Restart=on-failure

[Install]
WantedBy=default.target

ExecStart 에 emacs 실행 경로와 함께 넣어준 --fg-daemon 인자는 포어그라운드 데몬으로 실행하라는 의미이다. 참고로 --bg-daemon 혹은 --daemon 은 백그라운드 데몬으로 실행하라는 의미이다.

그리고 터미널에서 아래와 같이 실행하여 이맥스 서버를 실행시킨다.

systemctl enable --user emacs
systemctl start --user emacs

Launcher 아이콘 생성

런처 아이콘 역시 Emacs 26.2 소스 코드에서 만들어 놓은 파일을 고쳐서 사용하면 된다. emacs26.2/etc/emacs.desktop 을 아래와 같이 수정하여 .local/share/applications/emacs.desktop 을 만들어준다.

[Desktop Entry]
Name=Emacs
GenericName=Text Editor
Comment=Edit text
MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
Exec=emacsclient -c -a "emacs" %F
Icon=emacs
Type=Application
Terminal=false
Categories=Development;TextEditor;
StartupWMClass=Emacs
Keywords=Text;Editor;

emacsclient 의 -c 옵션은 새 이맥스 프레임을 생성하여 실행하라는 의미이고, -a 는 이맥스 데몬이 실행중이지 않을 경우 fallback 으로 할 액션에 대해 정의하는 것이다.

그리고 터미널에서 아래 명령어를 실행해 주면 finder 같은 곳에서 emacs를 검색하면 위에서 지정한 동작을 수행하는 아이콘이 뜰 것이다. 실행시켜주고 dock 에 고정하면 된다.

sudo desktop-file-install --dir=/usr/share/applications/ ~/.local/share/applications/emacs.desktop

'Emacs' 카테고리의 다른 글

Emacs - Python 문서 열람시 멈춤현상  (0) 2019.09.18
Emacs Wdired 모드  (0) 2019.06.29
macOS 에서 Emacs 데몬이 종료되지 않는 문제  (0) 2019.06.07
Create a new file in ido mode  (0) 2019.06.07
EIN and virtualenvwrapper  (0) 2018.07.22

ARM A32 명령어셋 VZIP

Programming

A32 명령어셋의 VZIP에 대해서 알아보자. 12.130 VZIP 을 참고했다.

이 명령어는 A64 버전의 ZIP1/ZIP2와 마찬가지로 두 벡터의 상위/하위 절반을 각각 떼어다가 뒤섞는다.

A32 버전의 VZIP은 특이하게도 Destination 피연산자가 따로 없다. 두 개의 피연산자를 갖는데 둘 다 상태가 변한다.

VZIP{cond}.size Qd, Qm
VZIP{cond}.size Dd, Dm
  • cond: 옵션. 조건처리.
  • size: 8 또는 16 또는 32.
  • Qd, Qm: 벡터 SIMD/FP 레지스터. Quadword.
  • Dd, Dm: 벡터 SIMD/FP 레지스터. Doubleword.

뒤섞는 방법은 아래 그림과 같다. 말하자면 A64 버전의 ZIP1ZIP2를 합쳐 놓은 것.

ARM A64 명령어셋 ZIP1, ZIP2

Programming

우선 ZIP1부터 정리한다. 이 명령어는 두 벡터에서, 즉 두 개의 SIMD/FP 레지스터에서 상위 절반(upper half)부분을 각각 떼어다가 뒤섞어(interleave) 붙인다. 설명보다는 그림으로 이해하는 것이 편할 듯.

ZIP1 <Vd>.<T>, <Vn>.<T>, <Vm>.<T>

위 그림에도 나와있듯이 ZIP2는 하위 절반 비트를 뒤섞는다.

뒤섞는 단위는 T파라미터에서 명시해 주는데, 8B, 16B, 4H, 8H, 2S, 4S, 2D 가 가능하다.