I am attempting to add non-interlaced GIF images other than 8-bit to a PDF document without having to fully decode the bitstream using PDF::Create for Perl.
The LZWDecode algorithm that is part of the PDF standard requires all images to have a minimum LZW code size of 8-bits, and PDF::Create is hard-coded to only embed 8-bit images.
So far, I have adapted the image loader from PDF::Create to read a 5-bit image and to fully decode the LZW stream. I am then able to use the encoder algorithm from PDF::Create to re-pack the image as 8-bit.
What I'd like to do is to eliminate the memory-intensive decode/encode step. This thread suggests that this is possible by "widening or shifting bits" to make LZW codes the proper length for LZWDecode.
I contacted the thread author and he provided some additional details, in particular that codes for color indices remain the same but are padded with zeros (e.g., [10000] becomes [000010000]), that <Clear> and <End> codes are changed to <256> and <257> respectively, and that all other codes are offset by 256 - original <Clear> code.
However, he was unable to elaborate further due to restrictions by his employer. In particular, I am uncertain how to handle a code when its modified value exceeds <4095> (the maximum index for the LZW code table). I am also unsure how to re-pack the revised codes into a bitstream.
The algorithms I am currently using are below.
# Read 5-bit data stream
sub ReadData5 {
my $data = shift;
my $c_size = 6; # minimium LZW code size
my $t_size = 33; # initial code table size
my ($i_buff,$i_bits) = (0,0); # input buffer
my ($o_buff,$o_bits) = (0,0); # output buffer
my $stream = ''; # bitstream
my $pos = 0;
SUB_BLOCK: while (1){
my $s = substr($data, $pos++, 1);
# get sub-block size
my $n_bytes = unpack('C', $s) or last SUB_BLOCK;
my $c_mask = (1 << $c_size) - 1;
BYTES: while (1){
# read c_size bits
while ($i_bits < $c_size){
# end of sub-block
!$n_bytes-- and next SUB_BLOCK;
$s = substr($data, $pos++, 1);
my $c = unpack('C', $s);
$i_buff |= $c << $i_bits;
$i_bits += 8;
}
# write c_size bits
my $code = $i_buff & $c_mask;
my $w_bits = $c_size;
$i_buff >>= $c_size;
$i_bits -= $c_size;
$t_size++;
if ($o_bits > 0){
$o_buff |= $code >> ($c_size - 8 + $o_bits);
$w_bits -= 8 - $o_bits;
$stream .= pack('C', $o_buff & 0xFF);
}
if ($w_bits >= 8){
$w_bits -= 8;
$stream .= pack('C', ($code >> $w_bits) & 0xFF);
}
if (($o_bits = $w_bits) > 0){
$o_buff = $code << (8 - $o_bits);
}
# clear code
if ($code == 32){
$c_size = 6;
$t_size = 33;
$c_mask = (1 << $c_size) - 1;
}
# end code
if ($code == 33){
$stream .= pack('C', $o_buff & 0xFF);
last SUB_BLOCK;
}
if ($t_size == (1 << $c_size)){
if (++$c_size > 12){
$c_size--;
} else {
$c_mask = (1 << $c_size) - 1;
}
}
}
}
# Pad with zeros to byte boundary
$stream .= '0' x (8 - length($stream) % 8);
return $stream;
}
#---------------------------------------------------------------------------
# Decode 5-bit data stream
sub UnLZW5 {
my $data = shift;
my $c_size = 6; # minimium LZW code size
my $t_size = 33; # initial code table size
my ($i_buff,$i_bits) = (0,0); # input buffer
my $stream = ''; # bitstream
my $pos = 0;
# initialize code table
my #table = map { chr($_) } 0..$t_size-2;
$table[32] = '';
my $prefix = '';
my $suffix = '';
# get first code word
while ($i_bits < $c_size){
my $d = unpack('C', substr($data, $pos++, 1));
$i_buff = ($i_buff << 8) + $d;
$i_bits += 8;
}
my $c2 = $i_buff >> ($i_bits - $c_size);
$i_bits -= $c_size;
my $c_mask = (1 << $i_bits) - 1;
$i_buff &= $c_mask;
# get remaining code words
DECOMPRESS: while ($pos < length($data)){
my $c1 = $c2;
while ($i_bits < $c_size){
my $d = unpack('C', substr($data, $pos++, 1));
$i_buff = ($i_buff << 8) + $d;
$i_bits += 8;
}
$c2 = $i_buff >> ($i_bits - $c_size);
$i_bits -= $c_size;
$c_mask = (1 << $i_bits) - 1;
$i_buff &= $c_mask;
# clear code
if ($c2 == 32){
$stream .= $table[$c1];
$#table = 32;
$c_size = 6;
$t_size = 33;
next DECOMPRESS;
}
# end code
if ($c2 == 33){
$stream .= $table[$c1];
last DECOMPRESS;
}
# get prefix and suffix
$prefix = $table[$c1] if $c1 < $t_size;
$suffix = $c2 < $t_size ? substr($table[$c2], 0, 1) : substr($prefix, 0, 1);
# write prefix
$stream .= $prefix;
# write multiple-character sequence
$table[$t_size++] = $prefix . $suffix;
# increase code size
if ($t_size == 2 ** $c_size){
if (++$c_size > 12){
$c_size--;
}
}
}
return $stream;
}
Doing one at a time is slow. Doing them all at once takes too much memory for you. Do them a chunk at a time.
my $BUFFER_SIZE = 5 * 50_000; # Must be a multiple of 5.
my $in_bytes = ...;
my $out_bytes = '';
while (my ($bytes) = $in_bytes =~ s/^(.{1,$BUFFER_SIZE})//s) {
# Unpack from 5 bit fields.
my #vals = map { pack('B*', "000$_") } unpack('B*', $bytes) =~ /(.{5})/g;
# Transform #vals into 8 bit values here.
# Pack to 8 bit fields.
$out_bytes .= pack('C*', #vals);
}
Since you're not transforming the values at all (just how they are stored), that simplifies to:
my $BUFFER_SIZE = 5 * 50_000; # Must be a multiple of 40.
my $in_bytes = ...;
my $out_bytes = '';
while (my ($bytes) = $in_bytes =~ s/^(.{1,$BUFFER_SIZE})//s) {
# Unpack from 5 bit fields.
my $bits = unpack('B*', $bytes);
$bits =~ s/(.{5})/000$1/g;
$out_bytes .= pack('B*', $bits);
}
You didn't say what to do with the extra bits. I simply ignored them.
Alternative approach with no bit string creation:
my $in_bytes = ...;
my $out_bytes = '';
while (my ($bytes) = $in_bytes =~ s/^(.{1,5})//s) {
my #bytes = map ord, split //, $bytes;
# 00000111 11222223 33334444 45555566 66677777
$out_bytes .= chr( (($bytes[0] >> 3) & 0x1F));
last if #bytes == 1;
$out_bytes .= chr((($bytes[0] << 2) & 0x1C) | (($bytes[1] >> 6) & 0x03));
$out_bytes .= chr( (($bytes[1] >> 1) & 0x1F));
last if #bytes == 2;
$out_bytes .= chr((($bytes[1] << 4) & 0x10) | (($bytes[2] >> 4) & 0x0F));
last if #bytes == 3;
$out_bytes .= chr((($bytes[2] << 1) & 0x1E) | (($bytes[3] >> 7) & 0x01));
$out_bytes .= chr( (($bytes[3] >> 2) & 0x1F));
last if #bytes == 4;
$out_bytes .= chr((($bytes[3] << 3) & 0x18) | (($bytes[4] >> 5) & 0x07));
$out_bytes .= chr( ( $bytes[4] & 0x1F));
}
The advantage of the above solution is that it's particularly efficient in C.
STRLEN in_len;
const char* in = SvPVbyte(sv, in_len);
STRLEN out_len = (in_len * 8 / 5) * 8;
char* out = (char*)malloc(out_len);
char* out_cur = out;
char* in_end = in + in_len;
while (in != in_end) {
*(out_cur++) = ((*in >> 3) & 0x1F));
if (++in == in_end) break;
*(out_cur++) = ((in[-1] << 2) & 0x1C) | ((*in >> 6) & 0x03));
*(out_cur++) = ((*in >> 1) & 0x1F));
if (++in == in_end) break;
*(out_cur++) = ((in[-1] << 4) & 0x10) | ((*in >> 4) & 0x0F));
if (++in == in_end) break;
*(out_cur++) = ((in[-1] << 1) & 0x1E) | ((*in >> 7) & 0x01));
*(out_cur++) = ((*in >> 2) & 0x1F));
if (++in == in_end) break;
*(out_cur++) = ((in[-1] << 3) & 0x18) | ((*in >> 5) & 0x07));
*(out_cur++) = ( *in & 0x1F));
}
return newSVpvn(out, out_len);
Related
The OVS documentation
... describes populating rules in the following format:
Range matches can be expressed as a collection of bitwise matches. For
example, suppose that the goal is to match TCP source ports 1000 to
1999, inclusive. The binary representations of 1000 and 1999 are:
01111101000
11111001111
The following series of bitwise matches will match 1000 and 1999 and
all the values in between:
01111101xxx
0111111xxxx
10xxxxxxxxx
110xxxxxxxx
1110xxxxxxx
11110xxxxxx
1111100xxxx
which can be written as the following matches:
tcp,tp_src=0x03e8/0xfff8
tcp,tp_src=0x03f0/0xfff0
tcp,tp_src=0x0400/0xfe00
tcp,tp_src=0x0600/0xff00
tcp,tp_src=0x0700/0xff80
tcp,tp_src=0x0780/0xffc0
tcp,tp_src=0x07c0/0xfff0
I'm trying to determine the correct way to generate those matches based on a minimum and maximum integer value in perl. I looked at the module Bit::Vector , but I wasn't able to figure out how to effectively use it for this purpose.
Let's pretend we trying to solve the equivalent problem for decimal for a second.
Say you want 567 (inclusive) to 1203 (exclusive).
Enlarging phase
You increment by 1 until you have the a multiple of 10 or you would exceed the range.
⇒598 (Creates 597-597)
⇒599 (Creates 598-598)
⇒600 (Creates 599-599)
You increment by 10 until you have a multiple of 100 or you would exceed the range.
You increment by 100 until you have a multiple of 1000 or you would exceed the range.
⇒700 (Creates 600-699)
⇒800 (Creates 700-799)
⇒900 (Creates 800-899)
⇒1000 (Creates 900-999)
You increment by 1000 until you have a multiple of 10000 or you would exceed the range.
[Would exceed limit]
Shrinking phase
You increment by 100 until you would exceed the range.
⇒1100 (Creates 1000-1099)
⇒1200 (Creates 1100-1199)
You increment by 10 until you would exceed the range.
You increment by 1 until you would exceed the range.
⇒1201 (Creates 1200-1200)
⇒1202 (Creates 1201-1201)
⇒1203 (Creates 1202-1202)
Same in binary, but with powers of 2 instead of powers of 10.
my $start = 1000;
my $end = 1999 + 1;
my #ranges;
my $this = $start;
my $this_power = 1;
OUTER: while (1) {
my $next_power = $this_power * 2;
while ($this % $next_power) {
my $next = $this + $this_power;
last OUTER if $next > $end;
my $mask = ~($this_power - 1) & 0xFFFF;
push #ranges, sprintf("0x%04x/0x%x", $this, $mask);
$this = $next;
}
$this_power = $next_power;
}
while ($this_power > 1) {
$this_power /= 2;
while (1) {
my $next = $this + $this_power;
last if $next > $end;
my $mask = ~($this_power - 1) & 0xFFFF;
push #ranges, sprintf("0x%04x/0x%x", $this, $mask);
$this = $next;
}
}
say for #ranges;
We can optimize that by taking advantage of the fact that we're dealing with binary.
my $start = 1000;
my $end = 1999 + 1;
my #ranges;
my $this = $start;
my $power = 1;
my $mask = 0xFFFF;
while ($start & $mask) {
if ($this & $power) {
push #ranges, sprintf("0x%04x/0x%x", $this, $mask);
$this += $power;
}
$mask &= ~$power;
$power <<= 1;
}
while ($end & ~$mask) {
$power >>= 1;
$mask |= $power;
if ($end & $power) {
push #ranges, sprintf("0x%04x/0x%x", $this, $mask);
$this |= $power;
}
}
say for #ranges;
Output:
0x03e8/0xfff8
0x03f0/0xfff0
0x0400/0xfe00
0x0600/0xff00
0x0700/0xff80
0x0780/0xffc0
0x07c0/0xfff0
I attempted to use the very elegant solution provided by #ikegami but found there were edge cases with resulting ports outside of the range or missing ports (e.g. 1-6, 1000-4000, 1000-10000). This alternative approach seems to avoid these issues.
my $LIMIT = 65535;
sub maxPort {
my ($port, $mask) = #_;
my $xid = $LIMIT - $mask;
my $nid = $port & $mask;
return $nid + $xid;
}
sub portMask {
my ($port, $end) = #_;
my $mask = $LIMIT;
my $test_mask = $LIMIT;
my $bit = 1;
my $net = $port & $LIMIT;
my $max_port = maxPort($net, $LIMIT);
while ($net && ($max_port <= $end)) {
$net = $port & $test_mask;
if ($net < $port) {
last;
}
$max_port = maxPort($net, $test_mask);
if ($max_port <= $end) {
$mask = $test_mask;
}
$test_mask -= $bit;
$bit <<= 1;
}
return $mask;
}
sub maskRange {
my ($start, $end) = #_;
my #portMasks;
if (($end <= $start) || ($end > $LIMIT)) {
exit 1;
}
my $mask = $LIMIT;
my $port = $start;
while ($port <= $end) {
$mask = portMask($port, $end);
push #portMasks, sprintf("0x%04x/0x%x", $port, $mask);
$port = maxPort($port, $mask) + 1;
}
return #portMasks;
}
my #ranges = maskRange(1000, 1999);
for (#ranges) {
print("$_", "\n");
}
Outputs:
0x03e8/0xfff8
0x03f0/0xfff0
0x0400/0xfe00
0x0600/0xff00
0x0700/0xff80
0x0780/0xffc0
0x07c0/0xfff0
I'm trying to understand elliptic curve cryptography by coding the algorithm in perl. But getting detailed information about implementing ECC is quite difficult.
So I gathered all informations together and started coding.
At first I searched for informations about a simple curve and I found it here: http://www.hjp.at/doc/rfc/rfc5639.html. Several curves from 160 upto 512 bits. Of course I started with the smallest one.
EDIT: Made a lot of mistakes, mostly due to the lack of information. But now this example works.
This is my perl script:
use bignum;
use Math::BigInt::Random;
my $p = hex "E95E4A5F737059DC60DFC7AD95B3D8139515620F";
my $a = hex "340E7BE2A280EB74E2BE61BADA745D97E8F7C300";
my $b = hex "1E589A8595423412134FAA2DBDEC95C8D8675E58";
my $x = hex "BED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3";
my $y = hex "1667CB477A1A8EC338F94741669C976316DA6321";
my $q = hex "E95E4A5F737059DC60DF5991D45029409E60FC09";
my $bitLength = 160;
sub point_doubling {
my ($px, $py) = #_;
return $px, $py if $px == 0 && $py == 0;
my $slope_numerator = 3 * $px * $px + $a;
my $slope_denominator = 2 * $py;
my $slope_denominator_inverse_modulo = $slope_denominator->bmodinv($p);
my $slope_inverse_product = $slope_numerator * $slope_denominator_inverse_modulo;
my $slope = $slope_inverse_product % $p;
my $rx = (($slope * $slope - 2 * $px)) % $p;
my $ry = (($slope * ($px - $rx) - $py)) % $p;
return ($rx, $ry);
}
sub point_addition {
my ($px, $py, $qx, $qy) = #_;
return point_doubling($px, $py) if $px == $qx && $py == $qy;
return $px, $py if $qx == 0 && $qy == 0;
return $qx, $qy if $px == 0 && $py == 0;
my $slope_numerator = $qy - $py;
my $slope_denominator = $qx - $px;
my $slope_denominator_inverse_modulo = $slope_denominator->bmodinv($p);
my $slope_inverse_product = $slope_numerator * $slope_denominator_inverse_modulo;
my $slope = $slope_inverse_product % $p;
my $rx = (($slope * $slope - $px - $qx)) % $p;
my $ry = (($slope * ($px - $rx) - $py)) % $p;
return ($rx, $ry);
}
sub scalar_product {
my ($k, $nx, $ny) = #_;
my ($qx, $qy) = (0, 0);
my $bit = 1;
for my $bitCounter (1..$bitLength) {
if ($k & $bit) {
($qx, $qy) = point_addition($nx, $ny, $qx, $qy);
}
($nx, $ny) = point_doubling($nx, $ny);
$bit <<= 1;
}
return ($qx, $qy);
}
sub check_point_on_curve {
my ($qx, $qy) = #_;
return $qy * $qy % $p == ($qx * $qx * $qx + $a * $qx + $b) % $p ||
$qy == 0 && $qx == 0;
}
print "Test if G is in curve:\n";
if (check_point_on_curve($x, $y)) {
print "G is a point on the curve\n";
} else {
die "ERROR: G is NOT a point on the curve\n";
}
# Creating the private key.
# 1.) A random integer dA with 0 < dA < q
my $dA = random_bigint(max => $q);
print "Private key chosen as $dA\n";
# 2.) Calculate the public key QA
my ($qax, $qay) = scalar_product($dA, $x, $y);
print "Public key calculated to ($qax, $qay)\n";
if (check_point_on_curve($qax, $qay)) {
print "Public key is a point on the curve\n";
} else {
die "ERROR: Public key is NOT a point on the curve\n";
}
The result looks likle this:
Test if G is in curve:
G is a point on the curve
Private key chosen as 14682691932678591389241030591930855095113892
Public key calculated to (1114548639616364108201749083649167655259366359581, 418437095262665064210367316915287142897094980157)
Public key is a point on the curve
Thanks for all your help.
I have a sequence and a number representing the location of a residue(character). I want to take 7 residues from each side of the residue. This is the code to do that:
my $seq = substr($sequence, $location-8, 14);
This grabs 7 from each side of the residue. However, there are some sequences where there is less than 7 residues on either side. So when this occurs, I get an error saying:
substr outside of string at test9.pl line 52 (#1) (W substr)(F) You tried to reference a substr() that pointed outside of a string. That is, the absolute value of the offset was larger than the length of the string.
How can I change the empty places and replace them with another letter (X for example).
For example, if there is a sequence
ABCDEFGH
and $location points to D, I need 7 on each side so the result would be:
XXXXABCDEFGHXXX
Expanding on my comment above. I would create a my_substr function that encapsulates the padding and location shift.
my $sequence = "ABCDEFGH";
my $location = 3;
sub my_substr {
my ($seq, $location, $pad_length) = #_;
my $pad = "X"x$pad_length;
return substr("$pad$seq$pad", $location, (2*$pad_length+1));
}
print my_substr($sequence, $location, 7) . "\n";
yields
XXXXABCDEFGHXXX
This is an very verbose answer, but more or less gets you what you want:
use strict;
use warnings;
my $sequence = 'ABCDEFGH';
my $wings = 7;
my $location = index $sequence, 'D';
die "D not found" if $location == -1;
my $start = $location - $wings;
my $length = 1 + 2 * $wings;
my $leftpad = 0;
if ($start < 0) {
$leftpad = -1 * $start;
$start = 0;
}
my $seq = substr($sequence, $start, $length);
$seq = ('X' x $leftpad) . $seq if $leftpad;
my $rightpad = $length - length ($seq);
$seq .= 'X' x $rightpad if $rightpad > 0;
print $seq;
Or to avoid all the extra work, could just create a new $sequence variable containing padding:
my $sequence = 'ABCDEFGH';
my $wings = 7;
my $location = index $sequence, 'D';
die "D not found" if $location == -1;
my $paddedseq = ('X' x $wings) . $sequence . ('X' x $wings);
my $seq = substr($paddedseq, $location, 1 + 2 * $wings);
print $seq;
I have written the following Perl script to generate the logical constraints in smt2 format to solve a sudoku puzzle for a given input file. The input file is in this format:
5 3 * * 7 * * * *
6 * * 1 9 5 * * *
* 9 8 * * * * 6 *
8 * * * 6 * * * 3
4 * * 8 * 3 * * 1
7 * * * 2 * * * 6
* 6 * * * * 2 8 *
* * * 4 1 9 * * 5
* * * * 8 * * 7 9
The big ugly Perl script is:
#! /usr/local/bin/perl
# CSC 410 A2 Q2
# Sudoku
use strict;
use warnings;
# All of the indices in a sudoku array
my #row1 = qw(r1c1 r1c2 r1c3 r1c4 r1c5 r1c6 r1c7 r1c8 r1c9);
my #row2 = qw(r2c1 r2c2 r2c3 r2c4 r2c5 r2c6 r2c7 r2c8 r2c9);
my #row3 = qw(r3c1 r3c2 r3c3 r3c4 r3c5 r3c6 r3c7 r3c8 r3c9);
my #row4 = qw(r4c1 r4c2 r4c3 r4c4 r4c5 r4c6 r4c7 r4c8 r4c9);
my #row5 = qw(r5c1 r5c2 r5c3 r5c4 r5c5 r5c6 r5c7 r5c8 r5c9);
my #row6 = qw(r6c1 r6c2 r6c3 r6c4 r6c5 r6c6 r6c7 r6c8 r6c9);
my #row7 = qw(r7c1 r7c2 r7c3 r7c4 r7c5 r7c6 r7c7 r7c8 r7c9);
my #row8 = qw(r8c1 r8c2 r8c3 r8c4 r8c5 r8c6 r8c7 r8c8 r8c9);
my #row9 = qw(r9c1 r9c2 r9c3 r9c4 r9c5 r9c6 r9c7 r9c8 r9c9);
my #rows = (\#row1, \#row2, \#row3, \#row4, \#row5, \#row6, \#row7, \#row8, \#row9);
# All of the indices in a sudoku block
my #block1 = qw(r1c1 r1c2 r1c3 r2c1 r2c2 r2c3 r3c1 r3c2 r3c3);
my #block2 = qw(r1c4 r1c5 r1c6 r2c4 r2c5 r2c6 r3c4 r3c5 r3c6);
my #block3 = qw(r1c7 r1c8 r1c9 r2c7 r2c8 r2c9 r3c7 r3c8 r3c9);
my #block4 = qw(r4c1 r4c2 r4c3 r5c1 r5c2 r5c3 r6c1 r6c2 r6c3);
my #block5 = qw(r4c4 r4c5 r4c6 r5c4 r5c5 r5c6 r6c4 r6c5 r6c6);
my #block6 = qw(r4c7 r4c8 r4c9 r5c7 r5c8 r5c9 r6c7 r6c8 r6c9);
my #block7 = qw(r7c1 r7c2 r7c3 r8c1 r8c2 r8c3 r9c1 r9c2 r9c3);
my #block8 = qw(r7c4 r7c5 r7c6 r8c4 r8c5 r8c6 r9c4 r9c5 r9c6);
my #block9 = qw(r7c7 r7c8 r7c9 r8c7 r8c8 r8c9 r9c7 r9c8 r9c9);
my #blocks = (\#block1, \#block2, \#block3, \#block4, \#block5, \#block6, \#block7, \#block8, \#block9);
open (FORMULA, ">", "sudoku.smt2") or die $!;
my $var;
my $i;
my $r;
my $c;
print (FORMULA "; Declare integers constants.\n");
for ($r = 0; $r < 9; $r++)
{
for ($c = 0; $c < 9; $c++)
{
print (FORMULA "(declare-const $rows[$r][$c] Int)\n");
}
}
print (FORMULA "\n");
print (FORMULA "; Assert for each variable r_ic_j that 1 <= r_ic_j <= 9\n");
for ($r = 0; $r < 9; $r++)
{
for ($c = 0; $c < 9; $c++)
{
print (FORMULA "(assert (not (and (not ($rows[$r][$c] = 1)) (not ($rows[$r][$c] = 2)) (not ($rows[$r][$c] = 3)) (not ($rows[$r][$c] = 4)) (not ($rows[$r][$c] = 5)) (not ($rows[$r][$c] = 6)) (not ($rows[$r][$c] = 7)) (not ($rows[$r][$c] = 8)) (not ($rows[$r][$c] = 9)))))\n");
}
}
print (FORMULA "\n");
print (FORMULA "; Assert that each row and column contains a number only once.\n");
for ($r = 0; $r < 9; $r++)
{
for ($c = 0; $c < 9; $c++)
{
$var = $rows[$r][$c];
for ($i = 0; $i < 9; $i++)
{
if ($var ne $rows[$r][$i])
{
print (FORMULA "(assert (not (= $var $rows[$r][$i])))\n");
}
}
for ($i = 0; $i < 9; $i++)
{
if ($var ne $rows[$i][$c])
{
print (FORMULA "(assert (not (= $var $rows[$i][$c])))\n");
}
}
}
}
print (FORMULA "\n");
print (FORMULA "; Assert that each number appears only once in each block.\n");
for ($r = 0; $r < 9; $r++)
{
for ($c = 0; $c < 9; $c++)
{
$var = $blocks[$r][$c];
for ($i = 0; $i < 9; $i++)
{
if ($var ne $blocks[$r][$i])
{
print (FORMULA "(assert (not (= $var $blocks[$r][$i])))\n");
}
}
}
}
print (FORMULA "\n");
print (FORMULA "; Declare input constants\n");
open (INPUT, "<", $ARGV[0]) or die $!;
my #lines;
my $line_num = 0;
while (#lines = split(/ /, <INPUT>))
{
for ($i = 0; $i <= $#lines; $i++)
{
chomp($lines[$i]);
if ($lines[$i] ne "*")
{
print (FORMULA "(assert (= $rows[$line_num][$i] $lines[$i]))\n");
}
}
$line_num++;
}
print (FORMULA "\n");
print (FORMULA "(check-sat)\n");
print (FORMULA "(get-model)\n");
close (FORMULA);
exit;
The portion of the script that is causing the seg fault is:
print (FORMULA "; Assert for each variable r_ic_j that 1 <= r_ic_j <= 9\n");
for ($r = 0; $r < 9; $r++)
{
for ($c = 0; $c < 9; $c++)
{
print (FORMULA "(assert (not (and (not ($rows[$r][$c] = 1)) (not ($rows[$r][$c] = 2)) (not ($rows[$r][$c] = 3)) (not ($rows[$r][$c] = 4)) (not ($rows[$r][$c] = 5)) (not ($rows[$r][$c] = 6)) (not ($rows[$r][$c] = 7)) (not ($rows[$r][$c] = 8)) (not ($rows[$r][$c] = 9)))))\n");
}
}
But this fragment just prints something like this:
(assert (not (and (not (x = 1)) (not (x = 2)) (not (x = 3)) ... (not (x = 9)))))
which should be logically equivalent to:
(assert (or (= x 1) (= x 2) (= x 3) ... (= x 9)))
Any advice is appreciated.
Regards.
The expression (x = 1) is not valid in SMT 2.0. It should be (= x 1). As you described in the comment above, after this change is made, the script works as expected.
For those not familiar with the game. You're given 8 numbers and you have to reach the target by using +, -, / and *.
So if the target is 254 and your game numbers are 2, 50, 5, 2, 1, you would answer the question correctly by saying 5 * 50 = 250. Then 2+2 is four. Add that on aswell to get 254.
Some videos of the game are here:
Video 1
video 2
Basically I brute force the game using by generating all perms of all sizes for the numbers and all perms of the symbols and use a basic inflix calculator to calculate the solution.
However it contains a flaw because all the solutions are solved as following: ((((1+1)*2)*3)*4). It doesn't permutate the brackets and it's causing my a headache.
Therefore I cannot solve every equation. For example, given
A target of 16 and the numbers 1,1,1,1,1,1,1,1 it fails when it should do (1+1+1+1)*(1+1+1+1)=16.
I'd love it in someone could help finish this...in any language.
This is what I've written so far:
#!/usr/bin/env perl
use strict;
use warnings;
use Algorithm::Permute;
# GAME PARAMETERS TO FILL IN
my $target = 751;
my #numbers = ( '2', '4', '7', '9', '1', '6', '50', '25' );
my $num_numbers = scalar(#numbers);
my #symbols = ();
foreach my $n (#numbers) {
push(#symbols, ('+', '-', '/', '*'));
}
my $num_symbols = scalar(#symbols);
print "Symbol table: " . join(", ", #symbols);
my $lst = [];
my $symb_lst = [];
my $perms = '';
my #perm = ();
my $symb_perms = '';
my #symb_perm;
my $print_mark = 0;
my $progress = 0;
my $total_perms = 0;
my #closest_numbers;
my #closest_symb;
my $distance = 999999;
sub calculate {
my #oprms = #{ $_[0] };
my #ooperators = #{ $_[1] };
my #prms = #oprms;
my #operators = #ooperators;
#print "PERMS: " . join(", ", #prms) . ", OPERATORS: " . join(", ", #operators);
my $total = pop(#prms);
foreach my $operator (#operators) {
my $x = pop(#prms);
if ($operator eq '+') {
$total += $x;
}
if ($operator eq '-') {
$total -= $x;
}
if ($operator eq '*') {
$total *= $x;
}
if ($operator eq '/') {
$total /= $x;
}
}
#print "Total: $total\n";
if ($total == $target) {
#print "ABLE TO ACCURATELY SOLVE WITH THIS ALGORITHM:\n";
#print "PERMS: " . join(", ", #oprms) . ", OPERATORS: " . join(", ", #ooperators) . ", TOTAL=$total\n";
sum_print(\#oprms, \#ooperators, $total, 0);
exit(0);
}
my $own_distance = ($target - $total);
if ($own_distance < 0) {
$own_distance *= -1;
}
if ($own_distance < $distance) {
#print "found a new solution - only $own_distance from target $target\n";
#print "PERMS: " . join(", ", #oprms) . ", OPERATORS: " . join(", ", #ooperators) . ", TOTAL=$total\n";
sum_print(\#oprms, \#ooperators, $total, $own_distance);
#closest_numbers = #oprms;
#closest_symb = #ooperators;
$distance = $own_distance;
}
$progress++;
if (($progress % $print_mark) == 0) {
print "Tested $progress permutations. " . (($progress / $total_perms) * 100) . "%\n";
}
}
sub factorial {
my $f = shift;
$f == 0 ? 1 : $f*factorial($f-1);
}
sub sum_print {
my #prms = #{ $_[0] };
my #operators = #{ $_[1] };
my $total = $_[2];
my $distance = $_[3];
my $tmp = '';
my $op_len = scalar(#operators);
print "BEST SOLUTION SO FAR: ";
for (my $x = 0; $x < $op_len; $x++) {
print "(";
}
$tmp = pop(#prms);
print "$tmp";
foreach my $operator (#operators) {
$tmp = pop(#prms);
print " $operator $tmp)";
}
if ($distance == 0) {
print " = $total\n";
}
else {
print " = $total (distance from target $target is $distance)\n";
}
}
# look for straight match
foreach my $number (#numbers) {
if ($number == $target) {
print "matched!\n";
}
}
for (my $x = 1; $x < (($num_numbers*2)-1); $x++) {
$total_perms += factorial($x);
}
print "Total number of permutations: $total_perms\n";
$print_mark = $total_perms / 100;
if ($print_mark == 0) {
$print_mark = $total_perms;
}
for (my $num_size=2; $num_size <= $num_numbers; $num_size++) {
$lst = \#numbers;
$perms = new Algorithm::Permute($lst, $num_size);
print "Perms of size: $num_size.\n";
# print matching symb permutations
$symb_lst = \#symbols;
$symb_perms = new Algorithm::Permute($symb_lst, $num_size-1);
while (#perm = $perms->next) {
while (#symb_perm = $symb_perms->next) {
calculate(\#perm, \#symb_perm);
}
$symb_perms = new Algorithm::Permute($symb_lst, $num_size-1);
}
}
print "exhausted solutions";
print "CLOSEST I CAN GET: $distance\n";
sum_print(\#closest_numbers, \#closest_symb, $target-$distance, $distance);
exit(0);
Here is the example output:
[15:53: /mnt/mydocuments/git_working_dir/countdown_solver$] perl countdown_solver.pl
Symbol table: +, -, /, *, +, -, /, *, +, -, /, *, +, -, /, *, +, -, /, *, +, -, /, *, +, -, /, *, +, -, /, *Total number of permutations: 93928268313
Perms of size: 2.
BEST SOLUTION SO FAR: (2 + 4) = 6 (distance from target 751 is 745)
BEST SOLUTION SO FAR: (2 * 4) = 8 (distance from target 751 is 743)
BEST SOLUTION SO FAR: (4 + 7) = 11 (distance from target 751 is 740)
BEST SOLUTION SO FAR: (4 * 7) = 28 (distance from target 751 is 723)
BEST SOLUTION SO FAR: (4 * 9) = 36 (distance from target 751 is 715)
BEST SOLUTION SO FAR: (7 * 9) = 63 (distance from target 751 is 688)
BEST SOLUTION SO FAR: (4 * 50) = 200 (distance from target 751 is 551)
BEST SOLUTION SO FAR: (7 * 50) = 350 (distance from target 751 is 401)
BEST SOLUTION SO FAR: (9 * 50) = 450 (distance from target 751 is 301)
Perms of size: 3.
BEST SOLUTION SO FAR: ((4 + 7) * 50) = 550 (distance from target 751 is 201)
BEST SOLUTION SO FAR: ((2 * 7) * 50) = 700 (distance from target 751 is 51)
BEST SOLUTION SO FAR: ((7 + 9) * 50) = 800 (distance from target 751 is 49)
BEST SOLUTION SO FAR: ((9 + 6) * 50) = 750 (distance from target 751 is 1)
Perms of size: 4.
BEST SOLUTION SO FAR: (((9 + 6) * 50) + 1) = 751
Here is Java applet (source) and Javascript version.
The suggestion to use reverse polish notation is excellent.
If you have N=5 numbers, the template is
{num} {num} {ops} {num} {ops} {num} {ops} {num} {ops}
There can be zero to N ops in any spot, although the total number will be N-1. You just have to try different placements of numbers and ops.
The (((1+1)+1)+1)*(((1+1)+1)+1)=16 solution will be found when you try
1 1 + 1 + 1 + 1 1 + 1 + 1 + *
Update: Maybe not so good, since finding the above could take 433,701,273,600 tries. The number was obtained using the following:
use strict;
use warnings;
{
my %cache = ( 0 => 1 );
sub fact { my ($n) = #_; $cache{$n} ||= fact($n-1) * $n }
}
{
my %cache;
sub C {
my ($n,$r) = #_;
return $cache{"$n,$r"} ||= do {
my $i = $n;
my $j = $n-$r;
my $c = 1;
$c *= $i--/$j-- while $j;
$c
};
}
}
my #nums = (1,1,1,1,1,1,1,1);
my $Nn = 0+#nums; # Number of numbers.
my $No = $Nn-1; # Number of operators.
my $max_tries = do {
my $num_orderings = fact($Nn);
{
my %counts;
++$counts{$_} for #nums;
$num_orderings /= fact($_) for values(%counts);
}
my $op_orderings = 4 ** $No;
my $op_placements = 1;
$op_placements *= C($No, $_) for 1..$No-1;
$num_orderings * $op_orderings * $op_placements
};
printf "At most %.f tries needed\n", $max_tries;