马尔可夫链奎因


17

这个问题将使用一个简单的马尔可夫模型。有关马尔可夫链的更多信息,请参见http://setosa.io/ev/markov-chains/

拿一根绳子。在此示例中,我们将使用以下单词:

reader

现在,对于每个字符,取字符串中每次出现该字符后出现的字符。(​`^`​代表字符串的开始并​`$`​代表结束)

`^` -> {'r'}       # After the start of the string, there is an `r`.
'r' -> {'e', `$`}  # After the first `r` (*r*eader), there is an `e`
                   # after the second (reade*r*), there is the end of the string.
'e' -> {'a', 'r'}
'a' -> {'d'}
'd' -> {'e'}

现在,从字符串的开头开始,从下一组字符中随机选择一个。附加此字符,然后从下一组字符中进行选择,依此类推,直到结束。以下是一些示例单词:

r
rereader
rer
readereader

如果一个字符多次出现在另一个字符之后,则更有可能被选择。例如,在cocoa cana之后c,有的机会有三分之二,而有的机会o有三分之一a

'c' -> {'o', 'o', 'a'}

挑战

创建一个不带输入的程序,并输出一个使用马尔可夫链生成的随机字符串,如上所述,其中链的输入是程序的源。

  1. 该程序必须至少包含两个字符,其中两个必须相同(以防止只有一个输出的“无聊”链)
  2. 您可以根据需要修改模型以使用字节而不是字符,但是在规则1中将“字符”更改为“字节”
  3. 该程序应以理论上的预期频率随机输出字符串

这是,所以最短的程序会获胜!


@ mbomb007所有信息都在问题中,如果您有兴趣,那么链接只是多余的(这是一个非常基本的实现)
Artyer

3
为什么在^$引号中?可以更清楚地将其排除在引号之外,或将其放在反引号中。
破坏的柠檬

Answers:


6

,64字节

很好玩

t:V Y\"m:"Yt@0T9=A OyY@>RC(t(Xy).'.)"ST["t:V Y"RPy";Vm"C9]\";Vm<tab>

<tab>代表文字的制表符(0x09)。在线尝试!

怎么样?

TL; DR:转义字符串语法,repr和eval。

对于需要包含文字"字符的字符串,Pip已将转义的字符串\"用作分隔符。使用转义字符串的标准quine如下所示:

V Y\""V Y".RPy\"

那就是:Yank(存储为y)一个包含"V Y".RPye 的字符串VRPy接受repr y,我们在其前面加上文字字符串V Y。最后,输出评估结果。

Markov quine的结构类似,只是我们要保存代码而不是输出代码,然后再对其进行一些处理。t:V Y\"...\"将评估结果分配给t。在评估的代码中,向m:"..."分配一个代码字符串m,我们将在末尾对其进行求值Vm

ST["t:V Y"RPy";Vm"C9] 建立一个包含

"t:V Y"  Literal string
RPy      Repr(y)
";Vm"    Literal string
C9       Tab character

并将其转换为字符串,默认情况下会串联所有项目。此部分等效"V Y".RPy于原始Quine。由于它是大eval字符串中的最后一个表达式,因此它的值是V运算符返回的值,因此是赋给的值t

因此,在评估和赋值之后,t等于完整代码,并且m包含

Yt@0T9=A OyY@>RC(t(Xy).'.)

现在将Vm其作为代码进行评估。让我们分解发生的事情。

                            We'll use y to hold the current character in the chain
Yt@0                        Yank first character of t into y (chain always starts there)
         Oy                 Output y without newline each time we...
    T9=A                    Loop till ASCII code of y equals 9 (tab)
                            Since there's only one literal tab, at the end of the program,
                              this satisfies the Markov chain ending requirement
                   Xy       Generate a regex that matches y
                  (  ).'.   Concatenate . to regex: now matches y followed by any character
                (t       )  Find all matches in t (returns a list)
              RC            Random choice from that list
           Y@>              Slice off the first character and yank the remaining one into y

几个注意事项:

  • 用文字选项卡结尾的代码比对“下一个字符或字符串结尾”进行正则表达式测试的时间短。
  • 如果代码中有双倍字符,我使用的正则表达式将无法正常工作;例如,将其应用于xxy只会返回,xx而不会返回xy匹配项。但是,幸运的是,此代码中没有双字符,因此没有关系。

8

JavaScript,217215字节

a="a=q;a=a.replace('q',uneval(a));for(b=c='a';d=a.split(c),c=d[Math.random()*~-d.length+1|0][0];b+=c);alert(b)";a=a.replace('q',uneval(a));for(b=c='a';d=a.split(c),c=d[Math.random()*~-d.length+1|0][0];b+=c);alert(b)

请注意,这使用uneval,仅Firefox支持。样品运行:

a=ale(a.lend[Ma=d[Macepla.ler(b+=c)b=q;fom(a=q;a=dort(b+1|0],c);a.lit(a)
at(c=c;d[0],c=q;ath+1|0][0];dorerac=ac=d[Ma),c;)*~-d[Ma=alenepl(b+=ac=c;a=c;d[2];d.re(c;fom()
a="a[0],und=d=a)
angt(b),und=d.l(b=a)
a)
ale(a.rth.revanepleplit(b)
ac);fore(b)*~-d.r(b+1|0];fora';a)*~-d.splalith+=dorth+=c=";ath+=a.length+=';ale(b)
a.r(b=c=a)b+1|0],und[0][0];d.splerath.spleneva)";ath.r(ceneplith+=d=aceple(c;)*~-d=';ala';)b='ac;fom(b=c;a.ler(b=d=d[Ma.rt(c=cendor()*~-d='a=";ac;a.spla)b=ceva=';a=d.rt(angt(alength+1|0],c;angt()
al(ac=dorth+1|0][0][0][0][Ma.split()

如您所见,它大部分都是乱码,但这是可以预料的;)OP创建了一个JSFiddle,它证明了输出在语法上有效的JS的可能性约为6.3%。


如果允许自读功能,则可能是78个字节的ES6:

f=(c="f",p=("f="+f).split(c),q=p[Math.random()*~-p.length+1|0][0])=>q?c+f(q):c

很少,很少输出语法上有效的JS:

f=>e?c+f():c
f=>e?c=>engt():c
f=>e?c=(e):c
f=>e?c=>e=>ength.split():c
f=p=>q?c+f():c
f(q).sp=",p[Mat(q?c=(),plith.lith.sp.sp[0]).lendom().lith+f=>q=p.lendom(",p=p=>q?c+f():c
f(q),q?c=(c=(q)*~-p[0]):c
f().random(),q?c=(c=p[0]):c
f=>q?c=(q="+f"+f).rath.split(c):c
f="+1|0])=().lith.rat()*~-p=>q?c=p[Mat(c=",q?c=p.rath.splendom()*~-plength.splith.lendom(c):c

我最喜欢创建的函数名称是.splendom()split+ length+ random


3
我想知道生成有效JavaScript的概率是多少。(书呆子警告)
DanTheMan '16

2
@DanTheMan当然非常非常低。所有括号和括号之间保持平衡的可能性极低。虽然有一次a.splerength.r(),这可能是有效的;)
ETHproductions'Nov

1
可能要注意,这仅是由于使用uneval而造成的FF
Shaun H

1
@ShaunH谢谢,我忘了只有FF支持uneval。
ETHproductions's

5
第二个自读功能无效(根据jsfiddle.net的说法,meta.codegolf.stackexchange.com/ a/ 4878/48878 “ quine不得直接或间接访问其自身的源。”)和@DanTheMan是无效的。 / kabkfLak / 1,机会应该在6.3%左右。
Artyer

5

Perl,103个字节

基于标准奎因和我对这个问题的回答

$_=q{$_="\$_=q{$_};eval";@s='$';{push@s,(@n=/(?<=\Q$s[-1]\E)(.|$)/g)[rand@n];$s[-1]&&redo}print@s};eval

示例输出

$_=q{$_=q{$_=";@sh@s=";eval
$_="\$_=q{$_='$_=q{$_}pus=\$_=";@n=";@ndo};{pus=';edo};@n]\Q$_};{$_};@s=q{$_=';@s[rand@s=/g)(@s,(@s,(@sh@s[-1];@ndo};ed@s[-1]\E)(.|$_}prevan]&ral";evan];{$_}pus='$_};ed@sh@sh@s[-1]\$_='$_};evando};eval
$_=q{$_=";ed@s[-1];evand@s="\Q$_=";@s[-1]\Q$_=q{$_=";@nd@sh@sh@s='$_=q{$_=q{$_='$_="\Q$_='$_};{pus=\$_=q{$_}pral
$_=";evando};@nd@sh@s,(@n]\$_=";@s,(@s[-1];{$_=q{$_}pral
$_=";eval
$_='$_=q{$_="\$_="\Q$_=";ed@sh@s=\E)(.|$_=q{$_=q{$_=q{$_=q{$_}pus=/(?<=q{$_};eval
$_=";ed@sh@s[-1]\Q$_=';edo};{$_=q{$_=";@nt@s,(@n]&&&&&&&ral";@nd@s,(@s[-1]\$_}pus=\E)(.|$_=';@nt@s[ral

与另一个问题类似,一些结果会生成有效的Perl:

$_=q{$_};{$_};eval";@sh@s[-1]\$_='$_};evan]\Q$_}preval";eval
$_=q{$_};{$_=q{$_=';@nd@s=q{$_};@s[-1]\E)(@s[-1]\E)(@n=';edo};{$_}predo};eval
$_=q{$_=q{$_};edo};@n=q{$_=q{$_};@s[rin='$_=q{$_}pus=/g)(.|$_=q{$_};edo};eval
$_=q{$_};eval
$_=q{$_=";@ndo};{$_}preval

但机会略低,约为2%。


7
如果您告诉我第一个示例是有效的Perl,我相信您。
ankh-morpork '16

2
@ dohaqatar7我起初误解了您的评论,并认为如果我说主要代码有效的Perl,您将不相信我...:D zoitz.com/comics/perl_small.png
大教堂黑斯廷斯

@ ankh-morpork:它显然是无效的,q{是字符串文字的开头,没有}可以关闭它的地方。Perl在运行随机字节序列方面实际上是相当糟糕的(而且,当这样做时,通常是由于早期的字符串文字或注释引起的)。

4

MS-DOS机器代码(.COM文件),63字节-非竞争

禁止竞争,因为木马不得访问其自己的源代码。

126字节的变体将满足“不访问其自身的源代码”的要求!

63字节的变体如下所示:

FC BE 00 01 AC 50 88 C2 B4 02 CD 21 E8 1A 00 59
4E AC 81 FE 3F 01 7C 03 BE 00 01 38 C1 75 F2 FE
CA 75 EE 81 FE 00 01 75 DB 8A 16 00 80 31 C0 8E
D8 31 C9 AC 00 C2 E2 FB 0E 1F 88 16 00 80 C3

我也不确定随机发生器的概率分布:

该程序利用以下事实:将时钟计数器和其他由中断修改的信息存储在段0中以生成随机数。

生成的输出示例如下:

FC BE 00 01 7C 03 BE 00 80 C3

FC BE 00 01 38 C1 75 F2 FE 00 80 31 C9 AC 81 FE 00 80 C3

FC BE 00 01 38 C1 75 EE 81 FE 00 01 38 C1 75 EE 81 FE CA
75 F2 FE 00 01 75 F2 FE 00 80 C3

FC BE 00 C2 B4 02 CD 21 E8 1A 00 01 7C 03 BE 00 59 4E AC
81 FE 3F 01 AC 81 FE 3F 01 7C 03 BE 00 01 7C 03 BE 00 01
AC 81 FE 3F 01 7C 03 BE 00 80 C3

转换为汇编代码的程序如下所示:

    cld                # Ensure SI is being incremented
    mov si, 0x100      # Move SI to the first byte of the program
nextOutput:
    lodsb              # Load one byte of the program ...
    push ax            # ... save it to the stack ...
    mov dl, al         # ... and output it!
    mov ah, 2
    int 0x21
    call pseudoRandom  # Create a random number (in DL)
    pop cx             # Take the stored byte from the stack
    dec si             # Go back to the last byte loaded
nextSearch:
    lodsb              # Load the next byte
    cmp si, programEnd # If we loaded the last byte ...
    jl notEndOfProgram # ... the next byte to be loaded ...
    mov si, 0x100      # ... is the first byte of the program.
notEndOfProgram:
    cmp cl, al         # If the byte loaded is not equal to ...
                       # ... the last byte written then ...
    jne nextSearch     # ... continue at nextSearch!
    dec dl             # Decrement the random number and ...
    jnz nextSearch     # ... continue at nextSearch until the ...
                       # ... originally random number becomes zero.
    cmp si, 0x100      # If the last byte read was not the last byte ...
    jnz nextOutput     # ... of the program then output the next ...
                       # ... byte!

    # Otherwise fall through to the random number generator
    # whose "RET" instruction will cause the program to stop.        

    # The random number generator:
pseudoRandom:
    mov dl, [0x8000]   # Load the last random number generated
                       # (Note that this is uninitialized when
                       # this function is called the first time)
    xor ax, ax         # We use segment 0 which contains the ...
    mov ax, ds         # ... clock information and other data ...
                       # ... modified by interrupts!
    xor cx, cx         # Prepare for 0x10000 loops so ...
                       # ... all bytes in the segment are processed ...
                       # ... once and the value of SI will be ...
                       # ... unchanged in the end!
randomNext:
    lodsb              # Load one byte
    add dl, al         # Add that byte to the next random number
    loop randomNext    # Iterate over all bytes
    push cs            # Restore the segment
    pop ds
    mov [0x8000], dl   # Remember the random number
    ret                # Exit sub-routine

programEnd:

不竞争是指满足挑战标准但使用的语言或功能比挑战新的答案。发布不读取其自身来源的变体,或删除答案。
mbomb007 '17

4

C,306 328 585 611 615 623 673 707 个字节

源代码:

p[256][256]={0};char*X="p[256][256]={0};char*X=%c%s%c,Y[999],c,j,*a;main(){sprintf(Y,X,34,X,34);for(a=Y;*a;a++)p[*a][*(a+1)]++;for(j=*Y;putchar(c=j);)while(p[c][++j]<<16<rand());}",Y[999],c,j,*a;main(){sprintf(Y,X,34,X,34);for(a=Y;*a;a++)p[*a][*(a+1)]++;for(j=*Y;putchar(c=j);)while(p[c][++j]<<16<rand());}

加上换行符和空格以提高可读性/解释性:

01  p[256][256]={0};
02  char*X="p[256][256]={0};char*X=%c%s%c,Y[999],c,j,*a;main(){sprintf(Y,X,34,X,34);for(a=Y;*a;a++)p[*a][*(a+1)]++;for(j=*Y;putchar(c=j);)while(p[c][++j]<<16<rand());}",
03  Y[999],c,j,*a;
04  main(){
05      sprintf(Y,X,34,X,34);
06      for(a=Y;*a;a++)p[*a][*(a+1)]++;
07      for(j=*Y;putchar(c=j);)
08          while(p[c][++j]<<16<rand());
09  }

说明

Line 01p[][]保存一个字符跟另一个字符的计数。

Line 02X包含程序的源代码,以进行转义%c%s%c

Line 03Y将包含程序的文字源。cj*a是计数变量。

Line 05:设置Y为包含奎纳。

Line 06:计算中的字母出现次数p[][]

Line 07:打印当前状态。

Line 08:根据中的计数随机找到下一个字符p[][]

样本输出:

p[++);p[99]=Y;putfor(aind(a++j,*a+j=j,c][c,*an(arile(pr*Y,Y[256]<<1);)][*Y,Y;)wha+++j=*aintfor*Y;prin(a+j]=j][256<1)pr(a;a;f(p[char(Y;for());};a;ma;ma=%s%chain(Y;ar(j][256<<<1)p[256<<raile(cha][9]<rin(j,34,34,Y[256]+j,Y,34,Y,c=Y,*a;*a;for(){0}


1
您可以添加没有换行符和空格的版本,以便我们可以验证字节数吗?
史蒂文H.

1
是的,我在顶部添加了单行版本。

3

Ruby,152个字节

0;s="0;s=%p<<33
0until putc($/=Hash[[*(s%%s).chars.each_cons(2)].shuffle][$/])==?"<<33
0until putc($/=Hash[[*(s%s).chars.each_cons(2)].shuffle][$/])==?!

样本输出:

0;s.c($/=Has(s).ears(2).ch[*(2)=Hacontc(2).ears.eas=Has==Hars%putc($/]).ears%sh_chuffl puns=Hachach[$/==?!

要么

0;s.ch[*($/=%pufl puns($/=%s.shas($/=Harsh_chutilears)])].e]).s)=Hac($/=="<<33\ntile].chufffle][[$/=Hars%sh_c(2)=%p<<<<<33
0;s)].ears)=Hars).c(s).eacon0un0;sh_c($/][*(s.s=Hacons=?!

奎因使用通过进行字符串格式化的方法"s%s",并通过获取所有两个字符的片段,对其进行混洗,然后将它们转换为哈希字典来进行马尔可夫链,其中对于重复的键,最后一个外观定义了值。为了避免在开头添加额外的逻辑,我使用跟踪了最近输出的字符$/,该字符会自动初始化为换行符,并确保代码中的换行符始终以0开头,与代码开头的字符相同。最后,我操纵源代码,以便只有一个,!因此我们总是在爆炸之后结束,使用<<33不带文字的方式添加它。可以使用不可打印的一位数字字符代替ASCII 33,进一步打高尔夫球,但这似乎太烦人了。


4
p<<<<<33super-super-super-concat运算符?;-)
ETHproductions's

3
那就是“ waaaay小于”运算符。
mbomb007 '16

2
我喜欢这个词!第一个例子就是这样的事实,即有关对象是否Has(s).ears(2)使我发笑!
Dom Hastings

2

Rust,564个字节(不具有竞争力)

extern crate rand;fn main(){let t=("extern crate rand;fn main(){let t=", ";let mut s=format!(\"{}{:?}{}\",t.0,t,t.1).into_bytes();s.push(0);let mut r=rand::thread_rng();let mut c=s[0];while c!=0{print!(\"{}\",c as char);let u=s.windows(2);c=rand::sample(&mut r,u.filter(|x|x[0]==c),1)[0][1];}}");let mut s=format!("{}{:?}{}",t.0,t,t.1).into_bytes();s.push(0);let mut r=rand::thread_rng();let mut c=s[0];while c!=0{print!("{}",c as char);let u=s.windows(2);c=rand::sample(&mut r,u.filter(|x|x[0]==c),1)[0][1];}}

由于我已经为另一个问题写了一个非常简洁的Rust quine,所以我认为我已经适应了它,因为它看起来很简单。尽管原件很小,但为此,我很少尝试减小尺寸。这是扩展版本以说明发生了什么:

// Random numbers are removed from the standard library in Rust,
// I had to make a cargo project to even compile this...
// Rust is hardly a golfing language.
extern crate rand;

fn main(){

    // The quine is fairly simple, we just make a tuple with 
    // "everything before this tuple" as first element, and
    // "everything after this tuple" with any quotes escaped 
    // as second. That makes it really easy to print.
    let t=("[...before...]", "[...after...]");

    // Instead of printing it, we save it as a byte vector
    // and append 0
    let mut s=format!("{}{:?}{}",t.0,t,t.1).into_bytes();
    s.push(0);

    // Start with the first character
    let mut c=s[0];
    let mut r=rand::thread_rng();

    while c!=0 {
        print!("{}",c as char);

        // We slide a 2 wide window over it to save a vector
        // of all bigrams. 
        let u=s.windows(2);

        // Filter it to only those which have the current character 
        // as first. Take one at random, its second is our next 
        // character.
        c=rand::sample(&mut r, u.filter(|x|x[0]==c), 1)[0][1];

        // Keep at it until the 0 byte is generated.
    }
}

样本输出1:

eran(),0{ller=samarin chas c).pr,teteran mut madoletet manthilaplerng().wind_byt.wit();let.u.0][*s=[*s.plleas.wshit, rnd:Vec<_byte mputextet ut t leat=r,t rant!=r().filllet rng();lar("{}{let.ind_byt.what amusarando_ramut!=st ct!(\").0]=colet!(&lec<_ret.plec=s.whrararandormpr=saile ret=r,0]=r);le(\"),t und;fint.prilt!();ler(2).forap(&ler=s(),t ut rat mu:t=ramund:Ve s.putec==[0];wst and_byt sh(\"et c s[1), munwhras[0];c=s=s="etornws(2)[0, ain(|x|x[0,0,0];fowile c ct(&l=",tes().co_byt().wrmat ash(|x|x[*s.lethrant.wrarmu.file(\"et, r==[1);uterile().0,t ando_rinwhas=[0{}"ect.wilant!("{ple mut, mut mamprmant,0];le(&lec=s.1),t co_>=fin mamustec!(\",c=[0];}}",0];leteteat.ust(",ternwhashrarmut ler("erat,0]==file and_reter==s.utet an letet.ut=", ras.1);fin("{:?}"et t letes[*sado_bytet rnd::Verain s[0];whant(){}{}\"echin s(2);lerad;wst reth(\",t u.iletermat c 1];}{}

样本输出2:

et!().0][0][0{}

2

Python 2,211字节

将结果输出到stderr

import random;X='q=[(list(t)+["$$"])[i+1]for i in range(len(t))if t[i]==c];c=random.choice(q)\nif c=="$$":exit(o)\no+=c\nexec X';s='import random;X=%r;s=%r;q=t=s%%(s,X);o=c="i";exec X';q=t=s%(s,X);o=c="i";exec X

在线尝试

样本输出:

i+[(s,X)));exenit(or;q=rt(t(t(t);o='ic\n(q)+1]=c\ndor randort))\ngeno));X)\nge(st))ic]=";oic=%ran(s%%(s%rt(q)\ngexe(s=st(t[(s=[if X=%(ompoiforanom;e(t X="$"$"ic="$"i";X=c rt X

简要说明:

  • 该程序使用s='s=%r;print s%%s';print s%squine格式。我创建了一个字符串s,其中将包含整个程序。
  • 该字符串X包含要递归执行的过程。
  • 该过程将构建输出字符串o,该字符串将stderr在到达马尔可夫链的末尾时打印到该字符串。
  • 链的末端由字符串表示 $$,使用两个字符,以便该程序适用于所有字符串。我本可以在程序中使用一个字符,例如chr(0),但我认为那会更长。
  • 每次执行时选择的字符都放在 c,(连同一起o)被初始化为程序的第一个字符。
  • 出现c在字符串中的每个选择项之后的字符列表t(保存源代码的变量的字符串)是q,供下次选择时使用c

1

PHP,144个 135 130 120 272 220 212字节

<?$e='$p=$n="";foreach(str_split($s)as$w)$p=$m[$p][]=$w;do echo$n=$m[$n][array_rand($m[$n])];while("\n"!=$n);
';$s='<?$e=%c%s%1$c;$s=%1$c%s%1$c;$s=sprintf($s,39,$e,$s);eval($e);';$s=sprintf($s,39,$e,$s);eval($e);

或者,为便于阅读而格式化:

<?$e='$p = $n = "";
foreach (str_split($s) as $w) {
    $p = $m[$p][] = $w;
}
do {
    echo $n = $m[$n][array_rand($m[$n])];
} while ("\n" != $n);
';$s='<?$e=%c%s%1$c;$s=%1$c%s%1$c;$s=sprintf($s,39,$e,$s);eval($e);';$s=sprintf($s,39,$e,$s);eval($e);

样本输出:

<?p=')ay_r_gecorr_splililen]=$p=$w;

和:

<?p=$n=$ntststs$m[$n=$m[ay_r_chondo$n=$ph(s$nt(fitstr_r_geantentr_s('m[$n=$n"!=$p etstsp][$w;d(fililile(s$w)$nt(sphor_str_getrarast(''''m[$n='m[$m';

和:

<?p=$who eay_re($n=$n=$nt(')];d(fililileando et($m[]=$pleay_ch(')aray_ren='''))ay_st_r_s($m[$m[asp])ay_co$m[$p $phorentechitr_rean)][$n=$nd("\n"!=$n=$wh(filend('')ay_gen=$ndo$nt_rasp=$n][$p=$whp=$n='m[$n"\n)))))][$w;dorechph(';dorracho$ple_s$w;fil

和:

<?ph($n);

PHP作弊,117

出于好奇,如果我们通过阅读自己的资料来作弊,我们可以做117:

<?=$p=$n='';foreach(str_split(file('m')[0])as$w)$p=$m[$p][]=$w;do echo$n=$m[$n][array_rand($m[$n])];while("\n"!=$n);

欢迎光临本站!不幸的是,对于这样的挑战,我们有一些规则作为适当的Quine,不幸的是,禁止从您自己的来源阅读。
发布Rock Garf Hunter,2017年

哦,很好,谢谢。我一直在寻找规则。我将不得不对此进行修改。
遮阳伞
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.