隐秘方块


14

隐秘方块

您的工作是接受一个字符串,并生成一个NxN表示该字符串的图像。您还必须编写将图像取入并将其转换回字符串的算法。得分将包括两种算法的字节数:

“加密”算法+“解密”算法

您应该分别发布每个文件,并分别显示加密和解密算法的字节数。


示例算法

例如,下面是“蓝色频道”中使用基于ASCII的简单隐写算法的“编程难题和代码高尔夫”:

#2e7250,#6ea972,#04eb6f,#0fc767,#74ab72,#ee6161
#b73b6d,#1aae6d,#f37169,#bda56e,#1fe367,#e99620
#706450,#0d3575,#146b7a,#4ea47a,#2a856c,#95d065
#3f2d73,#cef720,#bab661,#d1b86e,#f22564,#12b820
#0f3d43,#c86e6f,#1ee864,#a66565,#247c20,#c3bb47
#0e296f,#89d46c,#585b66,#c08f20,#455c20,#136f20

实际图片(该算法生成的图像。

图像爆炸。

您可以看到蓝色通道仅保留此图像的ascii值:

50 =  80(P) 72 = 114(r) 6f = 111(o) 67 = 103(g) 72 = 114(r) 61 =  97(a) 
6d = 109(m) 6d = 109(m) 69 = 105(i) 6e = 110(n) 67 = 103(g) 20 =  32( ) 
50 =  80(P) 75 = 117(u) 7a = 122(z) 7a = 122(z) 6c = 108(l) 65 = 101(e) 
73 = 115(s) 20 =  32( ) 61 =  97(a) 6e = 110(n) 64 = 100(d) 20 =  32( ) 
43 =  67(C) 6f = 111(o) 64 = 100(d) 65 = 101(e) 20 =  32( ) 47 =  71(G) 
6f = 111(o) 6c = 108(l) 66 = 102(f) 20 =  32( ) 20 =  32( ) 20 =  32( )

其余通道保留随机生成的值以“增添”图像中的各种颜色。将消息从图像中拉回时,我们可以简单地忽略其他通道值,并在蓝色通道中拉出十六进制位,从而重建字符串:

"Programming Puzzles and Code Golf"

请注意,用于在正方形中填充字符串的空格未包含在最终的解密输出中。虽然您必须在图像中填充字符串,但是可以假设输入字符串不会以空格结尾。


规则

  • 您必须为每个像素编码1个字符,选择用于编码char的通道是任意的。
  • 其他RGB颜色的通道必须是随机的,而不是您选择将字符串编码到的通道。这意味着您最终的非编码频道必须介于0x0000-0xFFFF(随机选择)之间。
  • 将最终结果表示为RGB颜色值的2D数组很好0x000000-0xFFFFFF,无需使用图像创建,除非您想玩它或它的字节数更少。如果选择输出为十六进制字符串,请在十六进制字符串前面加上#EG #FFFFFF#05AB1E。您可以使用制表符,逗号或其他任何在水平方向上有意义的分隔符,但必须保持正方形图案。换句话说,您必须使用适当的换行符分隔。
  • 输出必须为正方形,并且字符串末尾必须用空格填充以适应此情况。这意味着N≈SQRT(Input#Length())。如果输入长度不是理想的平方,则应四舍五入N并用空格填充。
  • 如前所述,如果在图像中填充空格,则不得在最终的“解密”输出中包括填充字符。
  • 您可以假设:
    • 输入字符串不会以空格结尾。
    • 输入字符串将仅使用可打印的ASCII字符。
  • 这是,最低字节数获胜。

只是为了澄清,解决方案必须每个像素精确地编码/解码一个字符?
ETHproductions 2016年

@ETHproductions听起来像是一个很好的后续挑战,但出于竞争目的,您选择一个编码通道并为每个像素编码1个字符。
魔术章鱼缸

我可能不会使用它,但是:是否可以用多余的空间“覆盖”图像?可以确定图像将具有与编码器生成的图像相同数量的重叠吗?

@ ais523我看不到这种方法会做什么,但是需要更多的字节来实现。我将一意孤行,因为挑战太老了,无法进行那样的大改变。
魔术章鱼缸

1
是的,我不确定原始问题中是否允许这样做,而不是建议进行更改。(我正在考虑将输入打包成一个矩形,这比将其打包成一个正方形要容易,因此可能比字节打包后再使字节填充更短,然后将矩形填充到一个更大的正方形。)

Answers:


2

05AB1E,34 + 12 = 46字节

使用红色通道。
05AB1E使用CP-1252编码。

编码:

DgDtî©n-Äð×JvyÇh`4F15Ý.Rh«}})'#ì®ä

D                                   # duplicate input
 gDtî©n-Ä                           # abs(len(input)-round_up(sqrt(len(input)))^2)
         ð×J                        # join that many spaces to end of input
            v                       # for each char in string
             yÇ                     # get ascii value
               h`                   # convert to base-16 number
                 4F                 # 4 times do:
                   15Ý.Rh           # push random base-16 number
                         «          # concatenate
                          }}        # end inner and outer loop
                            )       # wrap in list
                             '#ì    # prepend a "#" to each element in list
                                ®ä  # split in pieces round_up(sqrt(len(input))) long

在线尝试!

解码:

˜vy3£¦HçJ}ðÜ

˜               # deep flatten input to a list
 v              # for each color in the list
  y3£           # take the first 3 chars
     ¦          # remove the hash sign
      H         # convert from base-16 to base-10
       ç        # get the ascii char with that value
        J       # join to string
         }      # end loop
          ðÜ    # remove trailing spaces

在线尝试!

字节数相等的替代填充方法

Dgð×J¹gtî©n£

根据问题,我认为您必须专门加入换行符?(尽管您的答案即使适合于这样做,也可能会击败我,因为我只用了五个字节来处理问题的这一部分,而您的

@ ais523:规则指出2D阵列还可以。我是否以某种方式误解了?
Emigna '16

“您可以使用制表符,逗号或任何其他在水平上有意义的分隔符,但必须保持正方形图案;换句话说,必须使用适当的换行符分隔。” 强烈暗示它必须是字符串,因为2D数组本身并不包含换行符。换句话说,我将“数组”解释为描述输出的形状,而不是描述输出的数据类型。

@ ais523:我已要求OP进行澄清。就像您说的那样,实现起来并不是什么大的改变,但是如果不需要格式化,也许您也可以节省一些字节。
Emigna '16

@ ais523都可以接受。
魔术章鱼缸

4

C,201(编码)+ 175(解码)= 376字节

编码:

E(char*J){size_t L=ceil(sqrt(strlen(J)));int U;srand(time(NULL));for(int i=0;i<L;i++){for(int f=0;f<L;f++){printf("#%02X%02X%02X ",rand()%256,(U<strlen(J))?(int)J[U]:32,rand()%256);U+=1;}printf("\n");}}

将输入字符串的每个字符编码为RGB光谱的绿色通道,同时将其他两个通道设置为随机十六进制值。将通过STDIN的输入作为字符串,并将以方形的十六进制颜色代码的多行字符串输出到STDOUT。假设您已经安装了Python 3和ImageMagick,并且上面的文件被编译为a.out当前工作目录(CWD)中命名的文件,则可以Output.png使用以下命令从文本输出中直接将生成的图像命名为CWD:

./a.out "<Multiline Input>"|python3 -c "import sys,subprocess;Input=sys.stdin.read();print('# ImageMagick pixel enumeration: {0},{0},255,rgb\n'.format(len(Input.split('\n')[1].split()))+'\n'.join(['%d,%d:(%d,%d,%d)'%(g,i,int(j[1:][:2],16),int(j[1:][2:4],16),int(j[1:][4:6],16))for g,h in enumerate(Input.split('\n'))for i,j in enumerate(h.split())]))"|convert - -scale 1000% Output.png

这是上面的逗号使用Programming Puzzles and Code Golf输入字符串创建的示例输出图像:

样本输出

解码:

D(int c,char**U){char T[c];for(int Y=1;Y<c;Y++){char G[2]={U[Y][3],U[Y][4]};T[Y-1]=(char)strtol(G,NULL,16);}int C=c-1;T[C]='\0';while(T[C]==' '){T[C]='\0';C-=1;}printf("%s\n",T);}

通过STDIN接受一系列以空格分隔的十六进制颜色代码字符串,每个字符串用双引号(")(char** argvin main)括起来,并且在调用in mainint argc用于整数输入。将代表解码消息的单行/多行字符串输出到STDOUT。

我将尽可能地在任何时间和地点尝试更多的高尔夫运动。


另外,如果将这两种方法都放在同一个文件中,则可以使用以下main方法将其与每个函数放在一起以获取正确的输入:

int main(int argc,char**argv){if(strcmp(argv[1],"E")==0){Encode(argv[2]);}else{Decode(argc,argv);}}

并使用它,对于编码,您必须提供E作为第一个参数来调用编码方法,后跟单个字符串参数,而对于解码,您需要提供的只是用空格分隔的十六进制颜色代码字符串序列,每个序列都包含在其中双引号(")。


最后,如果你愿意,你可以得到充分的准备,随时可以使用的版本,在这里,虽然没有golfed,也不会输出任何在编译时警告或错误。


3

Python 2,164160 + 94 93 = 253字节

通过使用Wheat Wizard,节省了1 + 1字节。

-5字节归功于Kade

编码器图片编码器:字符串必须用引号引起来,例如"CodeGolf",输出是彩色ascii PPM图像。

from random import*
s=input()
n=int((len(s)-1)**0.5)+1
s=s.ljust(n*n)
r=randint
print"P3 %d %d 255 "%(n,n)+''.join("%d "*3%(r(0,255),r(0,255),ord(c))for c in s)

解码器图片解码器:将输入文件名作为命令行参数

from sys import*
print''.join(chr(int(c))for c in open(argv[1]).read().split()[6::3]).strip()

用法:

 python golf_stegansquare_enc.py > stega.ppm

 python golf_stegansquare_dec.py stega.ppm

例:

编程难题和代码高尔夫编程难题和代码高尔夫

洛普伊普森Lorem ipsum dolor坐着,consetetur sadipscing elitr,sed diam nonumy eirmod tempor invitunt ut labour et dolore magna aliquyam erat,sed diam voluptua。在Vero eos etAccusam et Justo duo dolores et ea rebum。Stet clita kasd gubergren,没有大海,不会坐着。Lorem ipsum dolor坐着,consetetur sadipscing elitr,sed diam nonumy eirmod tempor invitunt ut labour et dolore magna aliquyam erat,sed diam voluptua。在Vero eos etAccusam et Justo duo dolores et ea rebum。Stet clita kasd gubergren,没有大海,不会坐着。


您可以删除封闭的括号之间的空间,然后for
发布Rock Garf Hunter,2016年

@ETHproductions:sqrt(25-1)= sqrt(24)<5和>4 int。因此从这是4,然后+1编辑,所以5
Karl Napf

哦,我不好,我没看到-1
ETHproductions 2016年

1
您可以删除解码器之间print'之中的空间。我也很确定您可以int((len(s)+1)**.5)节省一些字节。
卡德

1
我要删除我之前的评论的最后一句话,但是您可以通过更改' '.join("%d %d %d"为来缩短打印时间,''.join(3*"%d "因为我敢肯定尾随空格是可以的。
卡德

2

Scala,97 + 68 = 165个字节

加密(97字节):

s=>s.map(_+((math.random*65535).toInt<<8)).iterator.grouped(math.sqrt(s.size)toInt)withPadding 32

接受一个String并重新调整一个整数序列的迭代器。

解密(68字节):

a=>" +$".r.replaceAllIn(a.flatten.map(h=>(h&0xFF)toChar)mkString,"")

接受一个整数序列的迭代器并返回一个字符串。

说明:

s=>                         //define an anonymous function
  s.map(                      //map each char of the string
    _+(                         //to the ascii value plus
      (math.random*65535).toInt)  //a random integer between 0 and 65535
      <<8                         //shifted 8 bits to the left
    )
  )
  .iterator                     //create an iterator
  .grouped(                     //group them in groups of size...
    math.sqrt(s.size)toInt        //sqrt of the size of the input, rounded up
  )withPadding 32               //pad with spaces to make a square

a=>
  " +$"              //take this string
  .r                 //parse it as a regex
  .replaceAllIn(     //replace every occurence of the regex in...
    a.flatten          //a flattened
    .map(h=>           //each element mapped
      (h&0xFF)toChar)    //to the character of the lower 8 bits
    mkString,          //joined to a string
    ""               //with an empty string
  )

2

Perl(103 + 1)+(36 + 2)= 142字节

如果要处理输入字符串中的换行符,则必须使用文本到图像编码器(-p以1字节的罚款;-p0(以罚款的另外一个字节)运行):

$_.=$"while($a=(length)**.5)=~/\./;$_=unpack"H*";s/../sprintf"#%04x$&,",rand+4**8/eg;s/(.*?\K,){$a}/
/g

图像到文本解码器(-p0以2字节的罚款运行):

$\.=chr hex for/..\W/g;$\=~s/ *$//}{

这使用#abcdef基于文本的图像格式,并在蓝色通道中进行编码。这是Programming Puzzles and Code Golf作为输入给出的可能输出的示例:

#b4d250,#bccb72,#43f06f,#4d6767,#74ba72,#269461
#e4f26d,#f63d6d,#701c69,#bbf56e,#6ef967,#d78d20
#4e0d50,#9b2775,#afd37a,#12a47a,#63e46c,#0e9565
#4cad73,#e43420,#6da761,#5a306e,#8fba64,#58f720
#d52443,#b4446f,#fbaf64,#4a4365,#1a5020,#f3ea47
#354c6f,#52cb6c,#11a766,#4c380a,#553820,#b31120

编码器说明:

$_.=$"             # append a space ($") to the input ($_)
  while            # as long as the following condition holds:
(($a=length)**.5)  # the square root of the input length (save this in $a)
=~/\./;            # has no decimal points in its string represenation
$_=unpack"H*";     # convert the input from base-256 to hexadecimal
s/../              # replace two characters of the input
  sprintf          # with a string formed from the template
  "#%04x$&,",      # four hex digits, the two matched characters, and a comma
  rand+4**8        # those hex digits are a random number from 0 to 4**8 (= 65536)
/eg;               # and do this for every non-overlapping match
s/(.*?             # find the minimum number of characters needed to match
   \K,)            # replacing the part of the match after the last matched comma
  {$a}/            # a string containing $a commas
/gx                # with a newline, for every non-overlapping match

我真的很高兴能用到\K工作成果;它指定了要替换的位置,并将其放置在循环中,似乎最重要的是在最后一次循环迭代中出现。因此,s/(.*?\K,){$a}/\n/g将匹配以下形式的最小长度字符串:任何逗号,任何逗号… 任何带有$a逗号的逗号,但是匹配的实际替换部分将只是最后一个逗号。这具有$a用换行符替换每个逗号的效果,从而为图像提供了正方形。

Perl应对这一挑战的最大优势(除了内置的字符串到十六进制转换器,这非常方便)是它的解码器非常短(事实上,尽管Perl具有内置的将十六进制转换为字符串,较短的时间是不使用它)。运作方式如下:

$\.=chr      # append to $\ the character code
  hex        # of the hexadecimal-string-to-number-translation
for/..\W/g;  # of each two characters that appear before a
             # non-alphanumeric character (not counting overlapping matches)
$\=~s/ *$//  # delete all spaces at the end of $\
}{           # in this context, this means "implicitly print $\,
             # prevent any other implicit printing"

紧接在非字母数字字符之前的两个字符的唯一实例是蓝色通道(我们要解压缩),它们出现在逗号和换行符的前面;以及出现在第一个字符之前的两个字符#。我们不希望使用后一种匹配项,但是它们不可避免地与前一种匹配项重叠,因此将被重叠的匹配项检查排除。


1

MySQL,438 + 237 = 675字节

在输出的末尾有尾随的新行,但是在解密后不会显示。十六进制函数(整数重载)会截断前导0,因此我必须用字符串0填充它。如果可以在定界符之间声明两个函数,则可以节省一些字节。

加密

delimiter //create function a(i text)returns text begin declare r int;declare q,p text;while mod(length(i),sqrt(length(i)))<>0 do set i:=concat(i,' ');end while;set r:=1;set q:="";while r<=length(i) do set p:=",";if mod(r,sqrt(length(i)))=0 then set p:="\r\n";end if;set q:=concat(q,'#',right(concat(0,hex(floor(rand()*256))),2),right(concat(0,hex(floor(rand()*256))),2),hex(mid(i,r,1)),p);set r:=r+1;end while;return q;end//
delimiter ;

解密

delimiter //create function b(i text)returns text begin declare x int;declare y text;set x:=0;set y:="";while instr(i,'#')>0 do set i:=substr(i,instr(i,'#')+5);set y:=concat(y,unhex(left(i,2)));end while;return trim(y);end//
delimiter ;

用法:

select a('test')
select b('#7D1874,#FFB465')
select b(a('test'))

1

C#,312 + 142 = 454字节

编码方式:

using System;I=>{var r=new Random();int i=I.Length;int N=(int)Math.Floor(Math.Sqrt(i))+1,S=N*N;while(i++<S){I+=' ';}var R="";for(i=0;i<S;){R+=i%N<1&i>0?"\n":i<1?"":" ";R+="#"+r.Next(256).ToString("X").PadLeft(2,'0')+r.Next(256).ToString("X").PadLeft(2,'0')+((int)I[i++]).ToString("X").PadLeft(2,'0');}return R;};

解码:

using System;I=>{var s=I.Replace('\n',' ').Split(' ');var R="";foreach(var t in s)R+=(char)System.Convert.ToInt32(t[5]+""+t[6],16);return R.TrimEnd(' ');};

完整程序:

using System;
class Steganographic
{
    static void Main()
    {
        Func<string, string> E = null;
        Func<string, string> D = null;

        E=I=>
        {
            var r=new Random();
            int i=I.Length;
            int N=(int)Math.Floor(Math.Sqrt(i))+1,S=N*N;
            while(i++<S){I+=' ';}
            var R="";
            for(i=0;i<S;)
            {
                R+=i%N<1&i>0?"\n":i<1?"":" ";
                R+="#"+r.Next(256).ToString("X").PadLeft(2,'0')+r.Next(256).ToString("X").PadLeft(2,'0')+((int)I[i++]).ToString("X").PadLeft(2,'0');
            }
            return R;
        };

        D=I=>
        {
            var s=I.Replace('\n',' ').Split(' ');
            var R="";
            foreach(var t in s)
                R+=(char)Convert.ToInt32(t[5]+""+t[6],16);
            return R.TrimEnd(' ');
        };

        string encoded = E("Programming Puzzles and Code Golf");
        Console.WriteLine(encoded);
        Console.WriteLine(D(encoded));

        encoded = E("Hello, World!");
        Console.WriteLine(encoded);
        Console.WriteLine(D(encoded));

        Console.Read(); // For Visual Studio
    }
}

1

Mathematica,111 + 65 = 176字节

编码器

Join[255~RandomInteger~{n=⌈Sqrt@Length@#⌉,n,2},ArrayReshape[#,{n,n,1},32],3]~Image~"Byte"&@*ToCharacterCode

解码器

StringTrim[""<>FromCharacterCode@ImageData[#,"Byte"][[;;,;;,3]]]&

1

处理中,220 209 194 + 171 167 151 = 391 380 376 361 345字节

更新:

删除无用 noStroke(),并使两个for循环的单语句器。

删除无用 image(p,0,0);,给了解密者文件名作为参数

加密演算法

void g(String h){int s=ceil(sqrt(h.length()));for(int y=0,x;y<s;y++)for(x=0;x<s;rect(x,y,1,1),x++)stroke(h.length()>y*s+x?h.charAt(y*s+x):32,random(255),random(255));get(0,0,s,s).save("t.png");}

调用函数: g("Programming Puzzles and Code Golf");

该函数需要一个String并创建输出,然后再将其另存为t.png。它使用red值存储隐藏的文本。

解密算法

void u(String f){PImage p=loadImage(f);f="";for(int j=0,i;j<p.height;j++)for(i=0;i<p.width;i++)f+=(char)red(p.get(i,j));print(f.replaceAll(" +$",""));}

调用函数: u(file_name);

这也是一个搜索由参数指定的图像,然后输出隐藏字符串的函数(因为它比返回字符串短)。

扩展代码

(加密演算法)

void g(String h) {
  int s=ceil(sqrt(h.length()));
  for(int y=0,x;y<s;y++)
    for(x=0;x<s;rect(x,y,1,1),x++)
      stroke(h.length()>y*s+x?h.charAt(y*s+x):32,random(255),random(255));
  get(0,0,s,s).save("t.png");
}

调用函数时将传递字符串。函数的第一行通过取ceil平方根的来计算平方的边长。然后,我们进入一个for循环,在其中将stroke(边缘的颜色)设置为将字符的ASCII值设置为红色,将随机值设置为蓝色和绿色。完成此操作后,我们创建一个rectwidth = 1和height = 的(矩形)1像素,即一个像素(出于某些奇怪的原因,我不能point正确使用)。在最后一行中,生成的图像然后另存为t.png

(解密算法)

void u(String f) {
  PImage p=loadImage(f);
  f="";
  for(int j=0,i;j<p.height;j++)
    for(i=0;i<p.width;i++)
      f+=(char)red(p.get(i,j));
  print(f.replaceAll(" +$",""));
}

此函数将文件名作为参数(作为字符串)。然后,文件中的图像将存储在变量中,以备后用。完成此操作后,我们将字符串设置为,""而不是仅创建一个新字符串以容纳隐藏的字符串。然后,我们通过两个嵌套的for循环遍历图像,并将像素红色值的字符值添加到字符串上。最后,我们从结果字符串中删除前导空格(使用正则表达式)后打印结果字符串。我们打印隐藏文本而不返回它的原因是因为这样可以缩短文本并节省字节。


加密的质询原始文本:

在此处输入图片说明


1

果冻,果冻代码页中的40 + 20 = 60字节

编码器(文字→图片):

”#;;ØHX¤¥4¡
»⁶x⁹²¤¤Ob⁴‘ịØHÇ€sj€”,Y
L½Ċç@

在线尝试!

解码器(图像→文本):

ḣ2ØHiЀ’ḅ⁴Ọ
ṣ”#Ç€œr⁶

在线尝试!

程序可能产生的示例输出(它将信息存储在红色通道中):

#504219,#720200,#6F38F1,#67055F,#7228C7,#61AC95
#6DD797,#6D20CB,#6962FA,#6E69B1,#67C41C,#209436
#50CB19,#75C9FC,#7A1B06,#7A695B,#6C5D5B,#6539A6
#735925,#20C80F,#612C38,#6EBF9E,#64C79E,#200915
#4337C5,#6F4704,#64FB5F,#65B2D1,#20E075,#47BC7C
#6F0C16,#6CD8EF,#66060B,#203C6C,#20D6E9,#20C0D7

在这些更大的挑战中,Jelly的简洁程度开始下降,需要几个“结构性”字符来解决解析歧义,但仍然非常简洁。编码器的工作方式如下:

Subroutine 1: convert digits to randomly padded hex string
”#;;ØHX¤¥4¡
”#;                     prepend #
    ØHX                 random hexadecimal digit
       ¤                parse ØH and X as a unit
   ;                    append
        ¥               parse ; and ØHX¤ as a unit
         4¡             repeat four times

Subroutine 2: convert string λ to square with size ρ
»⁶x⁹²¤¤Ob⁴‘ịØHÇ€sj€”,Y
 ⁶                      space
   ⁹²                   ρ squared
     ¤                  parse ⁹² as a unit
  x                     repeat string (i.e. ρ² spaces)
      ¤                 parse ⁶x⁹²¤ as a unit
»                       take maximum
Because space has the lowest value of any printable ASCII character,
this has the effect of padding λ to length ρ² with spaces.
       O                take codepoints of string
        b⁴              convert to base 16
           ịØH          use as indexes into a list of hexadecimal digits
          ‘             0-indexed (Jelly uses 1-indexing by default)
              ǀ        run subroutine 1 on each element
                s       split into groups of size ρ
                  €     inside each group
                 j ”,   join on commas
                     Y  join on newlines

Main program: basically just calculates ρ and lets subroutine 2 do the work
L½Ċç@
L                       length of input
 ½                      square rooted
  Ċ                     rounded up to the next highest integer
   ç@                   call subroutine 2 with the original input and the above

解码器的工作方式如下:

Subroutine: convert hexadecimal color string (without #) to character
ḣ2ØHiЀ’ḅ⁴Ọ
ḣ2                      take first two characters
  ØHi                   find indexes in a string of hexadecimal digits
     Ѐ                 for each of those characters
       ’                0-indexed (Jelly uses 1-indexing by default)
        ḅ⁴              convert from base 16
          Ọ             convert integer to character

Main program:
ṣ”#Ç€œr⁶
ṣ”#                     split on # signs
   ǀ                   run the subroutine for each element
     œr⁶                remove spaces from the right
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.