#!/usr/bin/perl

# Chaos Hashing
# (C) 2015 Michel Kuipers, (C) 2015 Domero, chaosje@gmail.com
# ALL RIGHTS RESERVED
# To use for free, notice me

sub getcode {
  my ($code) = @_;
  my $sum=0;
  my $n=$code; my $v=1;
  while ($n) {
    if ($n & 1) {
      $sum += $v
    }
    $n>>=1; $v++
  }
  return $sum & 15; 
  # here we keep 16/37 (43%) of the data and the result is pretty randomly..
  # (we want as much as possible randomly multiples to prevent backtracking!)
  # 0,1,2,3,3,4,5,6,4,5,6,7,7,8,9,10,5,6,7,8,8,9,10,11,9,10,11,12,12,13,14,15,6,7,8,
  # 9,9,10,11,12,10,11,12,13,13,14,15,0,11,12,13,14,14,15,0,1,15,0,1,2,2,3,4,5,7,8,9,
  # 10,10,11,12,13,11,12,13,14,14,15,0,1,12,13,14,15,15,0,1,2,0,1,2,3,3,4,5,6,13,14,
  # 15,0,0,1,2,3,1,2,3,4,4,5,6,7,2,3,4,5,5,6,7,8,6,7,8,9,9,10,11,12,8,9,10,11,11,12,
  # 13,14,12,13,14,15,15,0,1,2,13,14,15,0,0,1,2,3,1,2,3,4,4,5,6,7,14,15,0,1,1,2,3,4,
  # 2,3,4,5,5,6,7,8,3,4,5,6,6,7,8,9,7,8,9,10,10,11,12,13,15,0,1,2,2,3,4,5,3,4,5,6,6,
  # 7,8,9,4,5,6,7,7,8,9,10,8,9,10,11,11,12,13,14,5,6,7,8,8,9,10,11,9,10,11,12,12,13,
  # 14,15,10,11,12,13,13,14,15,0,14,15,0,1,1,2,3,4.
}

sub makecode {
  my ($pass) = @_;
  # make it very difficult to determine the length of the password!
  # the hashlength will depend on the length of the password and is SUM[1..length]/2
  my @res=();
  while (length($pass)) {
    for (my $c=0;$c<length($pass);$c++) {
      my $c=getcode(ord(substr($pass,$c,1)));
      push @res,$c
    }
    $pass=substr($pass,1)
  }
  # now fuck it up..
  my $len=0+@res; if ($len % 2) { unshift @res; $len-- }
  my $start=int $len/3; 
  my $end=int ($len<<1)/3;
  my @head=@res[0..$start-1];
  my @mid=@res[$start..$end-1];
  my @tail=@res[$end..$len-1];
  my @nibbles=(reverse @head,@mid,reverse @tail);
  my $bytes=[];
  for (my $n=0;$n<$len;$n+=2) {
    push @{$bytes},($nibbles[$n]<<4)+$nibbles[$n+1]
  }
  return $bytes
}

sub encode {
  my ($text,$pass) = @_;
  my $codes=makecode($pass); my $numcodes=0+@{$codes};
  my $out="";
  for (my $i=0;$i<length($text);$i++) {
    $out.=chr(ord(substr($text,$i,1)) ^ $codes->[$i % $numcodes])
  }
  return $out
}

my $pass="komjenooitachter"; 
my $text="Hello World!";

my $encode=encode($text,$pass);
print "Encoded = $encode\n";
my $decode=encode($encode,$pass);
print "Decoded = $decode\n"
