#!/usr/bin/perl

use warnings;
use strict;

my $usage = "";
$usage .= "usage: ./bind.pl n -- foo\n";

my $delta = 8;
my $n_cores = 64;

my $pr_only = 0;

# cannot use GetOpt because it would swallow the separator
my @new_argv = ();
foreach (@ARGV) {
    if ($_ eq '--print-only') {
	$pr_only = 1;
    } else {
	push @new_argv, $_;
    }
}
@ARGV = @new_argv;

if ($pr_only) {
    # in --print-only, we don't need "-- foo"
    if (@ARGV < 1) {
	die($usage);
    }
} else {
    # '-' separator is deprecated, but still here for backwards compatibility
    if (@ARGV < 3 or ($ARGV[1] ne '--' and $ARGV[1] ne '-')) {
	die($usage);
    }
}

sub is_natural_number {
    my ($v) = @_;

    if ($v =~ /^\d+$/ and $v > 0) {
	return 1;
    }
    return 0;
}

my $n_threads;
if (is_natural_number($ARGV[0])) {
    $n_threads = $ARGV[0];
} else {
    print STDERR "fatal: $ARGV[0] does not look like a natural number\n";
    die($usage);
}

sub taskset_list {
    my ($n) = @_;
    my @arr = ();

    for (my $i = 0; $i < $n; $i++) {
	push @arr, ($i * $delta) % $n_cores + (($i * $delta) / $n_cores) % $delta;
    }
    return @arr;
}

my $taskset_cmd = "taskset -c ".join(",", taskset_list($n_threads));
if ($pr_only and scalar(@ARGV) <= 2) {
    print "$taskset_cmd\n";
    exit 0;
}
my @args = @ARGV[2..scalar(@ARGV) - 1];

my $cmd = $taskset_cmd.' '.join(' ', @args);
if ($pr_only) {
    print "$cmd\n";
    exit 0;
}
exec($cmd) or print STDERR "exec '$cmd' failed: $!";
die;
