BigInteger 클래스 인 스 턴 스 의 구조 과정 - JDK 소스 코드 분석
17081 단어 JDK 소스 코드 분석
우선, 왜 큰 정수 류 가 필요 합 니까?쉽게 말 하면 내부 의 데이터 형식 이 표시 할 수 있 는 최대 수 는 64 비트 길이 이기 때문에 더 큰 길이 의 데이터 가 필요 할 때 기본 적 인 데이터 형식 은 처리 할 수 없다. 암호학 과 관련 된 암호 화 알고리즘 은 수백 비트 의 정수 와 관련 되 기 때문에 효과 적 인 데이터 구 조 를 설계 하여 이러한 수 요 를 만족 시 킬 수 있어 야 한다.
사실은 큰 정수 류 를 실현 하 는 것 도 어렵 지 않 습 니 다. 쉽게 생각해 보면 우 리 는 아주 긴 수 를 여러 개의 짧 은 수로 나 누 어 한 배열 에 저장 할 수 있 습 니 다. 대수 간 의 사 칙 연산 과 다른 연산 은 모두 배열 을 통 해 이 루어 집 니 다. JDK 는 이렇게 이 루어 집 니 다. JDK 의 BigInteger 류 에서 하나의 int 배열 로 데 이 터 를 저장 합 니 다.
/**
* The magnitude of this BigInteger, in big-endian order: the
* zeroth element of this array is the most-significant int of the
* magnitude. The magnitude must be "minimal" in that the most-significant
* int (mag[0]) must be non-zero. This is necessary to
* ensure that there is exactly one representation for each BigInteger
* value. Note that this implies that the BigInteger zero has a
* zero-length mag array.
*/
int[] mag;
이 int 배열 은 '0' 요소 로 시작 하지 않 습 니 다. 또한 이 종 류 는 이 수의 양 과 음 을 나타 내 는 속성 이 있 습 니 다.
/**
* The signum of this BigInteger: -1 for negative, 0 for zero, or
* 1 for positive. Note that the BigInteger zero must have
* a signum of 0. This is necessary to ensures that there is exactly one
* representation for each BigInteger value.
*
* @serial
*/
int signum;
1 은 이 수 를 플러스 로 나타 내 고 0 은 이 수 를 0 으로 나타 내 며 - 1 은 이 수 를 마이너스 로 나타 낸다.
본 고 에서 중점적으로 분석 한 구조 함 수 는 다음 과 같다.
/**
* Translates the String representation of a BigInteger in the specified
* radix into a BigInteger. The String representation consists of an
* optional minus sign followed by a sequence of one or more digits in the
* specified radix. The character-to-digit mapping is provided by
* Character.digit. The String may not contain any extraneous
* characters (whitespace, for example).
*
* @param val String representation of BigInteger.
* @param radix radix to be used in interpreting val.
* @throws NumberFormatException val is not a valid representation
* of a BigInteger in the specified radix, or radix is
* outside the range from {@link Character#MIN_RADIX} to
* {@link Character#MAX_RADIX}, inclusive.
* @see Character#digit
*/
public BigInteger(String val, int radix) {
이 구조 함 수 는 하나의 문자열 val 이 대표 하 는 큰 정 수 를 mag 배열 에 변환 하고 저장 하 는 것 입 니 다. 또한 val 이 대표 하 는 문자열 은 서로 다른 진법 (radix 결정) 일 수 있 습 니 다.
이 구조 함수 소스 코드 를 분석 하기 전에 먼저 문 제 를 생각해 보 세 요. 하나의 큰 정 수 를 구성 하 는 데 가장 중요 한 문 제 는 하나의 큰 수 를 mag 배열 에 어떻게 저장 하 느 냐 하 는 것 입 니 다. 보통 우리 가 실현 하면 배열 의 각 블록 에 한 자릿수 (대수 가 10 진법 이 라 고 가정) 를 저장 할 수 있 습 니 다. 그러나 이런 생각 을 하면 공간 을 너무 낭비 한 다 는 것 을 알 수 있 습 니 다. 왜냐하면 하나의 int 값 은 한 개의 10 진수 만 저장 할 수 있 기 때 문 입 니 다.
자바 언어 에서 모든 int 값 의 크기 범 위 는 - 2 ^ 31 ~ 2 ^ 31 - 1 즉 - 2147483648 ~ 2147483647 이 므 로 하나의 int 값 은 최대 10 비트 10 진법 의 정 수 를 저장 할 수 있 으 나 범 위 를 초과 하 는 것 을 방지 하기 위해 (22222222222 와 같은 int 는 저장 할 수 없습니다).보험 의 방식 은 모든 int 가 9 비트 의 10 진법 정 수 를 저장 하 는 것 이다. JDK 의 mag 배열 은 바로 이러한 보존 방식 이다. 따라서 한 줄 의 수가 189273483473895434934878 이면.
구분 후: 18927348 | 347389543 | 834934878. mag [0] 는 18927348 을 저장 하고 mag [1] 는 347389543 을 저장 하 며 mag [2] 는 834934878 을 저장 합 니 다. 이렇게 구분 하면 모든 int 값 을 최대 로 이용 하여 mag 배열 이 더 작은 공간 을 차지 할 수 있 습 니 다. 물론 이것 은 첫 번 째 단계 입 니 다.
구분 문 제 는 아직 끝나 지 않 았 습 니 다. 상기 구조 함 수 는 서로 다른 진법 의 수 를 지원 할 수 있 습 니 다. 최종 적 으로 mag 배열 안의 수 는 모두 10 진법 으로 바 뀌 었 습 니 다. 그러면 서로 다른 진법 의 큰 수 는 매번 에 구분 하 는 자릿수 가 다 릅 니 다. 만약 에 2 진법 을 선택 하면 매번 30 자 리 를 선택 하여 하나의 int 수 에 저장 할 수 있 습 니 다 (int 값 의 크기 범 위 는 - 2 ^ 31 에서 2 ^ 31 - 1). 만약 에 3 진법 3 ^ 19 < 21474836474847 < 3 ^ 20,따라서 매번 19 비트 를 선택 하여 하나의 int 수 에 저장 할 수 있 습 니 다. 서로 다른 진법 에서 선택 한 자릿수 가 다 르 기 때문에 하나의 배열 로 서로 다른 진법 이 선택 해 야 할 자릿수 를 저장 해 야 합 니 다. 그래서 다음 과 같 습 니 다.
private static int digitsPerInt[] = {0, 0, 30, 19, 15, 13, 11,
11, 10, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5};
이 배열 은 자바 가 지원 하 는 최대 최소 진법 에 대응 하 는 매번 구분 하 는 자릿수 를 저장 합 니 다.
이 구조 방법 에는 mag 배열 의 크기 를 초기 화 하 는 데 사용 되 는 배열 bits PerDigit 도 포함 되 어 있 습 니 다.
// bitsPerDigit in the given radix times 1024
// Rounded up to avoid underallocation.
private static long bitsPerDigit[] = { 0, 0,
1024, 1624, 2048, 2378, 2648, 2875, 3072, 3247, 3402, 3543, 3672,
3790, 3899, 4001, 4096, 4186, 4271, 4350, 4426, 4498, 4567, 4633,
4696, 4756, 4814, 4870, 4923, 4975, 5025, 5074, 5120, 5166, 5210,
5253, 5295};
자신 이 인터넷 에서 자 료 를 한참 동안 생각해 보고 나 서 야 알 게 되 었 는데, 지금 은 인터넷 의 한 단락 을 따 서 이 배열 의 의 미 를 설명 한다.
"bitsPerDigit 는 radix 진수 m 의 유효한 숫자 를 계산 하 는 데 사 용 됩 니 다." 2 진법 에 필요 한 bit 비트 [가설 에 필요 한 x 비트] 로 변환 합 니 다. 계산 식 을 살 펴 보 겠 습 니 다. radix ^ m - 1 = 2 ^ x - 1, 이 방정식 을 풀 려 면 x = m * log 2 (radix) 입 니 다. 현재 m 는 몇 자리 의 유효 숫자 이 고 상수 는 log 2 (radix) 밖 에 없습니다.이것 은 소수 입 니 다. 이것 은 우리 가 좋아 하 는 것 이 아 닙 니 다. 그래서 우 리 는 하나의 정수 로 표시 하고 싶 습 니 다. 그래서 우 리 는 그 를 1024 배 확대 한 다음 에 정 리 했 습 니 다. 예 를 들 어 3 진 bits PerDigit [3] = 1624 (나 는 계산기 로 x = log 2 (3) * 1024 ~ = 1623. xxx)"우리 팀 의 이 수 를 정 리 했 는데 왜 1624 를 취 했 습 니까? 사실은 너무 많 지 않 으 면 됩 니 다. 당신 은 1620, 1600, 1610 으로 설정 할 수 있 습 니 다."
즉, 일련의 수 (N 진법) 에 대해 서 는 바 이 너 리 의 자릿수 로 바 꾸 고 1024 를 곱 하면 bits PerDigit 배열 에 대응 하 는 데 이 터 를 곱 하고 1024 를 곱 하면 편 하 게 볼 수 있 습 니 다.
이상 의 소개 가 있 으 면 우 리 는 지금 이 방법의 소스 코드 를 붙 여 자세히 볼 수 있다.
public BigInteger(String val, int radix) {
int cursor = 0, numDigits;
int len = val.length();//
//
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
throw new NumberFormatException("Radix out of range");
if (val.length() == 0)
throw new NumberFormatException("Zero length BigInteger");
// , "-"
signum = 1;
int index = val.lastIndexOf("-");
if (index != -1) {
if (index == 0) {
if (val.length() == 1)
throw new NumberFormatException("Zero length BigInteger");
signum = -1;
cursor = 1;
} else {
throw new NumberFormatException("Illegal embedded minus sign");
}
}
// 0
while (cursor < len &&
Character.digit(val.charAt(cursor),radix) == 0)
cursor++;
if (cursor == len) {// 0, ZERO.mag
signum = 0;
mag = ZERO.mag;
return;
} else {//numDigits
numDigits = len - cursor;
}
//numDigits radix 2
//bitsPerDigit 1024 10 ( 1024),
// , 1
// int 32bit, 32 mag
int numBits = (int)(((numDigits * bitsPerDigit[radix]) >>> 10) + 1);
int numWords = (numBits + 31) /32;
mag = new int[numWords];
// digitsPerInt
// digitsPerInt[radix]
int firstGroupLen = numDigits % digitsPerInt[radix];
if (firstGroupLen == 0)
firstGroupLen = digitsPerInt[radix];
// mag
String group = val.substring(cursor, cursor += firstGroupLen);
mag[mag.length - 1] = Integer.parseInt(group, radix);
if (mag[mag.length - 1] < 0)
throw new NumberFormatException("Illegal digit");
//
int superRadix = intRadix[radix];
int groupVal = 0;
while (cursor < val.length()) {
group = val.substring(cursor, cursor += digitsPerInt[radix]);
groupVal = Integer.parseInt(group, radix);
if (groupVal < 0)
throw new NumberFormatException("Illegal digit");
destructiveMulAdd(mag, superRadix, groupVal);
}
mag = trustedStripLeadingZeroInts(mag);
}
현재 제 가 마지막 몇 줄 에 대해 분석 하지 않 은 것 은 intRadix 배열 이 있 기 때 문 입 니 다. 우 리 는 아직 설명 하지 않 았 기 때 문 입 니 다. intRadix 배열 은 사실은 각종 radix 에 대응 하 는 가장 좋 은 진법 을 저장 한 표 입 니 다. 위 에서 우 리 는 10 진법 에 대해 9 자리 수 를 한꺼번에 자 르 는 것 을 선택 했다 고 말 했 습 니 다. 그러면 하나의 int 변 수 를 충분히 이용 할 수 있 고 int 의 범 위 를 초과 하지 않도록 보장 할 수 있 기 때문에 intRadix [10]=10^9=1000000000. intRadix[3]=3^19=1162261467. 즉, 매번 절 취 된 수 는 radix 가 대응 하 는 가장 좋 은 진법 을 초과 하지 않 는 다. 예 를 들 어 10 진법 수 189273483473895434934878 의 최종 전환 은:
18927348 * (10 ^ 9) ^ 2 + 347389543 * (10 ^ 9) + 834934878, 최종 적 으로 mag 배열 은 10 ^ 9 진수 로 저 장 됩 니 다.
intRadix 는 다음 과 같 습 니 다.
private static int intRadix[] = {0, 0,
0x40000000, 0x4546b3db, 0x40000000, 0x48c27395, 0x159fd800,
0x75db9c97, 0x40000000, 0x17179149, 0x3b9aca00, 0xcc6db61,
0x19a10000, 0x309f1021, 0x57f6c100, 0xa2f1b6f, 0x10000000,
0x18754571, 0x247dbc80, 0x3547667b, 0x4c4b4000, 0x6b5a6e1d,
0x6c20a40, 0x8d2d931, 0xb640000, 0xe8d4a51, 0x1269ae40,
0x17179149, 0x1cb91000, 0x23744899, 0x2b73a840, 0x34e63b41,
0x40000000, 0x4cfa3cc1, 0x5c13d840, 0x6d91b519, 0x39aa400
};
intRadix[10]=0x3b9aca00 = 1000000000; intRadix[3]=0x4546b3db=1162261467;
우 리 는 numWords = (numBits + 31) / 32. 초기 배열 의 크기 는 큰 정수 로 구 분 된 숫자 가 아니 라 큰 정수 에 대응 하 는 이 진 자릿수 (31 을 더 해 numWords 가 0 이상 확보) 를 계산 한 다음 에 32 로 나 누 어 얻 을 수 있 기 때문에 mag 배열 의 모든 int 수의 32 자 리 는 완전히 이용 된다 는 것 을 알 게 되 었 다.즉, 모든 int 수 를 부호 없 는 숫자 로 볼 수 있 습 니 다. int 의 32 비트 를 완전히 이용 하지 않 으 면 우 리 는 분 단 된 결과 에 따라 mag 배열 의 초기 크기 를 확정 할 수 있 습 니 다. 이전의 예: 18927348 | 347389543 | 834934878. 우 리 는 10 진수 가 매번 9 비트 를 선택 할 때마다 경 계 를 넘 지 않 는 다 는 것 을 알 고 있 습 니 다. 우 리 는 mag 배열 의 크기 를 직관 적 으로 3 으로 얻 을 수 있 습 니 다.그러나 이렇게 되면 모든 int 요 소 는 여전히 빈 자 리 를 이용 하지 않 습 니 다.
따라서 우리 의 이전 구분 방법 은 전체 배열 이 초기 화 된 상상 에서 첫 번 째 단계 일 뿐 이 었 다. 이 예 는 numWords = (numBits + 31) / 32 로 계산 하면 마지막 에 얻 은 것 은 3 이 어야 한다. 그러나 좀 더 큰 꼬치 의 결과 가 반드시 같 지 않다 면 적 게 쌓 이 고 많은 꼬치 를 만 들 때 절약 하 는 공간 을 나 타 낼 수 있다.
자바 에는 기호 int 수가 없 기 때문에 mag 배열 에 서 는 항상 기호 가 마이너스 인 요소 가 있 습 니 다. 마지막 으로 원래 의 큰 정 수 를 mag 배열 에 저 장 된 radix 에 대응 하 는 최 적 진수 로 바 꾸 는 과정 은 destructiveMul Add 로 이 루어 집 니 다. 현재 구조 함수 의 마지막 부분 과 방법 destructiveMul Add 의 해석 을 첨부 합 니 다.
int superRadix = intRadix[radix];
int groupVal = 0;
while (cursor < val.length()) {
//
group = val.substring(cursor, cursor += digitsPerInt[radix]);
groupVal = Integer.parseInt(group, radix);//
if (groupVal < 0)
throw new NumberFormatException("Illegal digit");
//mag*superRadix+groupVal. :18927348*10^9+347389543
destructiveMulAdd(mag, superRadix, groupVal);
}
// mag 0, 0 .
mag = trustedStripLeadingZeroInts(mag);
private final static long LONG_MASK = 0xffffffffL;
// Multiply x array times word y in place, and add word z
private static void destructiveMulAdd(int[] x, int y, int z) {
// Perform the multiplication word by word
// y z long
long ylong = y & LONG_MASK;
long zlong = z & LONG_MASK;
int len = x.length;
long product = 0;
long carry = 0;
// y , , .
for (int i = len-1; i >= 0; i--) {
// x[i] long, 32
product = ylong * (x[i] & LONG_MASK) + carry;
//x[i] 32 .
x[i] = (int)product;
// 32 ,
carry = product >>> 32;
}
// Perform the addition
// z
//mag long z
long sum = (x[len-1] & LONG_MASK) + zlong;
//mag 32 .
x[len-1] = (int)sum;
// 32
carry = sum >>> 32;
//
for (int i = len-2; i >= 0; i--) {
sum = (x[i] & LONG_MASK) + carry;
x[i] = (int)sum;
carry = sum >>> 32;
}
}
전체 과정 에서 보존 하 는 방법 은 우리 의 머 릿 속 에 있 는 간단 한 저장 방법 과 다 르 기 때문에 최종 적 으로 mag 배열 의 요 소 는 원래 의 문자열 과 크게 다 를 것 이다. 그러나 실질 적 으로 똑 같은 수 를 나타 내 고 현재 189273483473895434934878 사례 의 구조 과정 을 보 여 준다.
초기 화 후 numBits = 87 로 계 산 됩 니 다. 이렇게 배열 은 크기 numWords = 3 을 초기 화 합 니 다. 최종 순환 에 들 어가 기 전 mag 배열: [0] [0] [18927348] 첫 번 째 순환 후: [0] [4406866] [-1295432089] (18927348*10^9+347389543) 두 번 째 순환 후: [1026053] [-1675546271] [440884830]. ((18927348*10^9+347389543)*10^9+834934878) 결국 우 리 는 189273483473895434934878 을 10 ^ 9 진법 으로 바 꾸 어 mag 배열 에 저장 했다. 최종 결 과 는 우리 에 게 익숙 하지 않 지만 그 중에서 몇 줄 로 나 누 는 방법 과 배열 이 공간 을 절약 하 는 사상 은 모두 배 울 만 한 것 이다. (잘 정리 되 어 수준 이 없 는 것 같다...)
지금 마지막 문제 가 있 습 니 다. 어떻게 mag 배열 을 원래 의 문자열 로 바 꿉 니까?JDK 에 서 는 계속 나 누 기 를 통 해 이 루어 집 니 다. BigInteger 류 의 인 스 턴 스 는 toString 방법 을 호출 할 때 원래 의 문자열 을 되 돌려 줍 니 다. 코드 는 다음 과 같 습 니 다.
public String toString(int radix) {
if (signum == 0)
return "0";
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
radix = 10;
// Compute upper bound on number of digit groups and allocate space
// , mag .
int maxNumDigitGroups = (4*mag.length + 6)/7;
String digitGroup[] = new String[maxNumDigitGroups];
// Translate number to string, a digit group at a time
BigInteger tmp = this.abs();
int numGroups = 0;
while (tmp.signum != 0) {
BigInteger d = longRadix[radix];
MutableBigInteger q = new MutableBigInteger(),
r = new MutableBigInteger(),
a = new MutableBigInteger(tmp.mag),
b = new MutableBigInteger(d.mag);
//a b q , r
a.divide(b, q, r);
BigInteger q2 = new BigInteger(q, tmp.signum * d.signum);
BigInteger r2 = new BigInteger(r, tmp.signum * d.signum);
//
digitGroup[numGroups++] = Long.toString(r2.longValue(), radix);
//
tmp = q2;
}
// StringBuilder , 0
// Put sign (if any) and first digit group into result buffer
StringBuilder buf = new StringBuilder(numGroups*digitsPerLong[radix]+1);
if (signum<0)
buf.append('-');
buf.append(digitGroup[numGroups-1]);
// Append remaining digit groups padded with leading zeros
for (int i=numGroups-2; i>=0; i--) {
// Prepend (any) leading zeros for this digit group
int numLeadingZeros = digitsPerLong[radix]-digitGroup[i].length();
if (numLeadingZeros != 0)
buf.append(zeros[numLeadingZeros]);
buf.append(digitGroup[i]);
}
return buf.toString();
}
/* zero[i] is a string of i consecutive zeros. */
private static String zeros[] = new String[64];
static {
zeros[63] =
"000000000000000000000000000000000000000000000000000000000000000";
for (int i=0; i<63; i++)
zeros[i] = zeros[63].substring(0, i);
}
상술 한 방법의 핵심 은 바로 a.divide(b, q, r). longRadix 배열 과 intRadix 배열 은 비슷 한 의 미 를 가진다.
intRadix [10] = 10 ^ 9. 따라서 longRadix [10] = 10 ^ 18 은 intRadix 에 대한 제곱, 즉 long 유형 에 있어 서 가장 좋 은 진수 에 해당 합 니 다.
간단하게 생각해 보면 알 수 있 듯 이 mag 배열 을 10 ^ 9 로 계속 나 누 면 834934878, 347389543, 18927348 을 얻 을 수 있 습 니 다. 10 ^ 18 (자바 가 이 수량 급 의 연산 을 지원 합 니 다) 을 나 누 면 두 번 에 각각 3473895431892348, 834934878 을 얻 을 수 있 기 때문에 longRadix 배열 로 연산 하 는 효율 이 더욱 높 습 니 다. 상기 방법 에 나타 난 Mutable BigInteger 는인터넷 의 한 단락 을 빌려 설명 하 는 것 이 내 가 말 한 것 보다 더 좋 을 것 이다.
"Mutable BigInteger 는 BigInteger 류 의 또 다른 버 전 입 니 다. 임시 대상 을 만 들 지 않 는 전제 에서 호출 프로그램 이 BigInteger 유형의 반환 값 을 얻 도록 하 는 것 이 특징 입 니 다. (가 변 대상 기술 이 라 고 함)... 큰 정수 나 누 기 는 대량의 다른 산술 조작 으로 이 루어 져 있 기 때문에 대량의 임시 대상 이 필요 하 며, 새로운 대상 을 만 들 지 않 고 많은 조작 을 완성 하면 프로그램의 성능 을 크게 개선 할 수 있 으 며, (대상 을 만 드 는 대가 가 매우 높 기 때문에) 자바 의 큰 정수 류 에 서 는 Mutable BigInteger 류 의 방법 으로 큰 정수 나 누 기 를 수행 할 수 있 습 니 다. "
가장 중요 한 divide 방법 은 죄송합니다. 제 가 오랫동안 봤 는데 도 코드 의 생각 을 이해 하지 못 했 습 니 다. 여러분 들 이 잘못된 방향 을 가르쳐 주 셨 으 면 좋 겠 습 니 다!
JDK 의 BigInteger 류 에서 우리 가 볼 만 한 많은 방법 을 실 현 했 습 니 다. 기본 적 인 네 가지 요 소 를 제외 하고 그 안에 소 수 를 판단 하 는 방법 도 제 공 했 습 니 다. 멱, 모델 링, 역 원, 최대 공약 수 를 구 했 습 니 다. Miller - Rabin 알고리즘 을 사 용 했 습 니 다. 미끄럼 창 알고리즘 은 빠 른 속도 로 멱 을 구 했 습 니 다.3000 여 줄 의 코드... 관심 이 있다 면 그 중 어떤 방법 이 우리 에 게 깨 우 침 이 있 을 수 있 습 니 다.
여러분 들 이 동의 하지 않 는 부분 이 있 으 면 지적 해 주시 기 바 랍 니 다. 많이 교류 하 세 요. 그리고 저 에 게 남 겨 진 divide 방법 을 설명해 주 시 는 것 이 좋 습 니 다. 저 는 오랫동안 보 았 더 니 징 그 러 워 요. 큰 신 이 어디 에 있 는 지!!!
본문 에서 참고 한 문장 링크:http://www.iteye.com/topic/1118707 .
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
BufferedWriter 소스 코드 분석다음으로 이동:http://www.fengfly.com/plus/view-214074-1.html Buffered Writer 는 버퍼 문자 출력 흐름 입 니 다.그것 은 Writer 에 계승 되 었 다.Buffer...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.