Merge branch 'multimode_clb' into fpga_spice
This commit is contained in:
commit
28f54961b2
|
@ -11,12 +11,12 @@ compiled_file="compiled_$benchmark"
|
|||
tb_formal_postfix="_top_formal_verification_random_tb"
|
||||
verilog_output_dirname="${benchmark}_Verilog"
|
||||
log_file="${benchmark}_sim.log"
|
||||
new_reg_sh="my_regression.sh"
|
||||
|
||||
|
||||
cd $fpga_flow_scripts
|
||||
|
||||
perl rewrite_path_in_file.pl -i $vpr_path/regression_verilog.sh
|
||||
perl rewrite_path_in_file.pl -i $vpr_path/VerilogNetlists/ff.v
|
||||
perl rewrite_path_in_file.pl -i $vpr_path/regression_verilog.sh -o $vpr_path/$new_reg_sh
|
||||
|
||||
cd $my_pwd
|
||||
|
||||
|
@ -28,7 +28,7 @@ rm -f $log_file
|
|||
rm -f $compiled_file
|
||||
|
||||
# Start the script -> run the fpga generation -> run the simulation -> check the log file
|
||||
source regression_verilog.sh
|
||||
source $new_reg_sh
|
||||
iverilog -o $compiled_file $verilog_output_dirname/SRC/$benchmark$include_netlists -s $benchmark$tb_formal_postfix
|
||||
vvp $compiled_file -j 16 >> $log_file
|
||||
|
||||
|
|
|
@ -137,6 +137,7 @@ enable_testing()
|
|||
add_subdirectory(yosys)
|
||||
add_subdirectory(abc)
|
||||
add_subdirectory(ace2)
|
||||
add_subdirectory(libs)
|
||||
add_subdirectory(vpr7_x2p)
|
||||
|
||||
# run make to extract compiler options, linker options and list of source files
|
||||
|
@ -175,8 +176,13 @@ set_target_properties(libace ace
|
|||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/ace2"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/ace2")
|
||||
|
||||
|
||||
# Set output locations to be in the main source tree under the relevant folder
|
||||
set_target_properties(libvtrutil
|
||||
PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libs/libvtrutil"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libs/libvtrutil"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libs/libvtrutil")
|
||||
|
||||
set_target_properties(libarchfpga read_arch
|
||||
PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/vpr7_x2p/libarchfpga"
|
||||
|
|
|
@ -330,7 +330,7 @@
|
|||
<port type="output" prefix="out" size="1"/>
|
||||
</spice_model>
|
||||
<!--DFF subckt ports should be defined as <D> <Q> <CLK> <RESET> <SET> -->
|
||||
<spice_model type="ff" name="static_dff" prefix="dff" spice_netlist="OPENFPGAPATHKEYWORD/vpr7_x2p/vpr/SpiceNetlists/ff.sp" verilog_netlist="OPENFPGAPATHKEYWORD/vpr7_x2p/vpr/VerilogNetlists/ff.v">
|
||||
<spice_model type="ff" name="static_dff" prefix="dff" spice_netlist="OPENFPGAPATHKEYWORD/vpr7_x2p/vpr/SpiceNetlists/ff.sp" verilog_netlist="FFPATHKEYWORD">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="on" spice_model_name="INV1X"/>
|
||||
<output_buffer exist="on" spice_model_name="INV1X"/>
|
||||
|
@ -372,7 +372,7 @@
|
|||
<port type="sram" prefix="mode" size="2" mode_select="true" spice_model_name="sc_dff_compact" default_val="1"/>
|
||||
</spice_model>
|
||||
<!--Scan-chain DFF subckt ports should be defined as <D> <Q> <Qb> <CLK> <RESET> <SET> -->
|
||||
<spice_model type="sff" name="sc_dff_compact" prefix="scff" spice_netlist="OPENFPGAPATHKEYWORD/vpr7_x2p/vpr/SpiceNetlists/ff.sp" verilog_netlist="OPENFPGAPATHKEYWORD/vpr7_x2p/vpr/VerilogNetlists/ff.v">
|
||||
<spice_model type="sff" name="sc_dff_compact" prefix="scff" spice_netlist="OPENFPGAPATHKEYWORD/vpr7_x2p/vpr/SpiceNetlists/ff.sp" verilog_netlist="FFPATHKEYWORD">
|
||||
<design_technology type="cmos"/>
|
||||
<input_buffer exist="on" spice_model_name="INV1X"/>
|
||||
<output_buffer exist="on" spice_model_name="INV1X"/>
|
||||
|
|
|
@ -1,172 +0,0 @@
|
|||
----------------------------------- Summary ------------------------------------
|
||||
Circuit: /research/ece/lnis/USERS/tang/github/OpenFPGA/fpga_flow/benchmarks/Blif/Test_Modes/test_modes
|
||||
Architecture: k6_N10_sram_chain_HC_template.xml
|
||||
Technology (nm): 45
|
||||
Voltage: 0.90
|
||||
Temperature: 85
|
||||
Critical Path: 5.8141e-09
|
||||
Size of FPGA: 2 x 2
|
||||
Channel Width: 200
|
||||
|
||||
----------------------------------- Warnings -----------------------------------
|
||||
No transistor counter function for BLIF model: .frac_lut6
|
||||
No transistor counter function for BLIF model: .subckt adder
|
||||
No transistor counter function for BLIF model: .subckt shift
|
||||
Attempted to search for a transistor with a capacitance smaller than the smallest in the technology file.
|
||||
|
||||
No dynamic power defined for BLIF model: .subckt adder
|
||||
No leakage power defined for BLIF model: .subckt adder
|
||||
No dynamic power defined for BLIF model: .frac_lut6
|
||||
No leakage power defined for BLIF model: .frac_lut6
|
||||
No dynamic power defined for BLIF model: .subckt shift
|
||||
No leakage power defined for BLIF model: .subckt shift
|
||||
|
||||
------------------------------- Power Breakdown --------------------------------
|
||||
Component Power (W) %-Total %-Dynamic Method
|
||||
|
||||
Total 0.0002701 1 0.7897
|
||||
Routing 0.0001289 0.4773 0.7668
|
||||
Switch Box 2.212e-05 0.08191 0
|
||||
Connection Box 0.0001068 0.3954 0.9256
|
||||
Global Wires 0 0 -nan
|
||||
PB Types 8.066e-05 0.2986 0.6884
|
||||
Primitives 4.913e-05 0.1819 0.8837
|
||||
Interc Structures 8.866e-06 0.03283 0.5489
|
||||
Buffers and Wires 2.266e-05 0.08389 0.3197
|
||||
Other Estimation Methods 0 0 -nan
|
||||
Clock 6.051e-05 0.224 0.9736
|
||||
|
||||
---------------------------- Power Breakdown by PB -----------------------------
|
||||
This sections provides a detailed breakdown of power usage by PB (physical
|
||||
block). For each PB, the power is listed, which is the sum power of all
|
||||
instances of the block. It also indicates its percentage of total power (entire
|
||||
FPGA), as well as the percentage of its power that is dynamic (vs. static). It
|
||||
also indicates the method used for power estimation.
|
||||
|
||||
The data includes:
|
||||
Modes: When a pb contains multiple modes, each mode is listed, with
|
||||
its power statistics.
|
||||
Bufs/Wires: Power of all local buffers and local wire switching
|
||||
(transistor-level estimation only).
|
||||
Interc: Power of local interconnect multiplexers (transistor-
|
||||
level estimation only)
|
||||
|
||||
Description of Estimation Methods:
|
||||
Transistor Auto-Size: Transistor-level power estimation. Local buffers and
|
||||
wire lengths are automatically sized. This is the default estimation
|
||||
method.
|
||||
Transistor Specify-Size: Transistor-level power estimation. Local buffers
|
||||
and wire lengths are only inserted where specified by the user in the
|
||||
architecture file.
|
||||
Pin-Toggle: Dynamic power is calculated using enery-per-toggle of the PB
|
||||
input pins. Static power is absolute.
|
||||
C-Internal: Dynamic power is calculated using an internal equivalent
|
||||
capacitance for PB type. Static power is absolute.
|
||||
Absolute: Dynamic and static power are absolutes from the architecture file.
|
||||
Sum of Children: Power of PB is only the sum of all child PBs; interconnect
|
||||
between the PB and its children is ignored.
|
||||
Ignore: Power of PB is ignored.
|
||||
|
||||
|
||||
Component Power (W) %-Total %-Dynamic Method
|
||||
|
||||
io 0 0 -nan Ignore
|
||||
clb 8.066e-05 0.2986 0.6884 Transistor Auto-Size
|
||||
Bufs/Wires 1.43e-05 0.05294 0.2804
|
||||
Interc: 8.462e-06 0.03133 0.542
|
||||
crossbar0 3.015e-06 0.01116 0.5188
|
||||
crossbar1 3.264e-06 0.01208 0.5568
|
||||
crossbar2 1.076e-06 0.003984 0.5484
|
||||
crossbar3 8.245e-07 0.003053 0.5364
|
||||
crossbar4 0 0 -nan
|
||||
crossbar5 0 0 -nan
|
||||
clks 0 0 -nan
|
||||
carry_in 2.821e-07 0.001045 0.6112
|
||||
fle 5.79e-05 0.2144 0.8106 Transistor Auto-Size
|
||||
Bufs/Wires 6.769e-06 0.02506 0.3446
|
||||
Mode:fle_phy 3.361e-05 0.1244 0.9139
|
||||
Interc: 0 0 -nan
|
||||
direct_clk 0 0 -nan
|
||||
mux1 0 0 -nan
|
||||
mux2 0 0 -nan
|
||||
frac_logic 3.388e-07 0.001254 0 Transistor Auto-Size
|
||||
Bufs/Wires 3.388e-07 0.001254 0
|
||||
Interc: 0 0 -nan
|
||||
mux1 0 0 -nan
|
||||
mux2 0 0 -nan
|
||||
frac_lut6 0 0 -nan Transistor Auto-Size
|
||||
Bufs/Wires 0 0 -nan
|
||||
adder_phy 0 0 -nan Transistor Auto-Size
|
||||
Bufs/Wires 0 0 -nan
|
||||
ff_phy 3.327e-05 0.1232 0.9232 Transistor Auto-Size
|
||||
Bufs/Wires 0 0 -nan
|
||||
Mode:n2_lut5 1.741e-05 0.06444 0.7925
|
||||
Interc: 0 0 -nan
|
||||
lut5inter 1.741e-05 0.06444 0.7925 Transistor Auto-Size
|
||||
Bufs/Wires 5.658e-07 0.002095 0.6977
|
||||
Interc: 0 0 -nan
|
||||
complete1 0 0 -nan
|
||||
ble5 1.684e-05 0.06235 0.7956 Transistor Auto-Size
|
||||
Bufs/Wires 0 0 -nan
|
||||
Mode:blut5 1.12e-05 0.04146 0.8091
|
||||
Interc: 0 0 -nan
|
||||
flut5 1.12e-05 0.04146 0.8091 Transistor Auto-Size
|
||||
Bufs/Wires 2.007e-07 0.000743 0.7628
|
||||
Interc: 2.646e-07 0.0009797 0.684
|
||||
mux1 2.646e-07 0.0009797 0.684
|
||||
lut5 1.655e-06 0.006127 0.2417 Transistor Auto-Size
|
||||
Bufs/Wires 0 0 -nan
|
||||
Mode:wire 0 0 -nan
|
||||
Interc: 0 0 -nan
|
||||
complete:lut5 0 0 -nan
|
||||
Mode:lut5 1.655e-06 0.006127 0.2417
|
||||
Interc: 0 0 -nan
|
||||
lut 1.655e-06 0.006127 0.2417 Transistor Auto-Size
|
||||
Bufs/Wires 0 0 -nan
|
||||
ff 9.079e-06 0.03361 0.9173 Transistor Auto-Size
|
||||
Bufs/Wires 0 0 -nan
|
||||
Mode:arithmetic 5.641e-06 0.02088 0.7689
|
||||
Interc: 0 0 -nan
|
||||
arithmetic 5.641e-06 0.02088 0.7689 Transistor Auto-Size
|
||||
Bufs/Wires 3.732e-07 0.001382 0.7081
|
||||
Interc: 1.399e-07 0.0005179 0.708
|
||||
sumout 1.399e-07 0.0005179 0.708
|
||||
lut4 7.913e-07 0.00293 0 Transistor Auto-Size
|
||||
Bufs/Wires 0 0 -nan
|
||||
Mode:wire 0 0 -nan
|
||||
Interc: 0 0 -nan
|
||||
complete:lut4 0 0 -nan
|
||||
Mode:lut4 7.913e-07 0.00293 0
|
||||
Interc: 0 0 -nan
|
||||
lut 7.913e-07 0.00293 0 Transistor Auto-Size
|
||||
Bufs/Wires 0 0 -nan
|
||||
adder 0 0 -nan Transistor Auto-Size
|
||||
Bufs/Wires 0 0 -nan
|
||||
ff 4.336e-06 0.01606 0.9163 Transistor Auto-Size
|
||||
Bufs/Wires 0 0 -nan
|
||||
Mode:n1_lut6 0 0 -nan
|
||||
Interc: 0 0 -nan
|
||||
ble6 0 0 -nan Transistor Auto-Size
|
||||
Bufs/Wires 0 0 -nan
|
||||
Interc: 0 0 -nan
|
||||
mux1 0 0 -nan
|
||||
lut6 0 0 -nan Transistor Auto-Size
|
||||
Bufs/Wires 0 0 -nan
|
||||
Mode:wire 0 0 -nan
|
||||
Interc: 0 0 -nan
|
||||
complete:lut6 0 0 -nan
|
||||
Mode:lut6 0 0 -nan
|
||||
Interc: 0 0 -nan
|
||||
lut 0 0 -nan Transistor Auto-Size
|
||||
Bufs/Wires 0 0 -nan
|
||||
ff 0 0 -nan Transistor Auto-Size
|
||||
Bufs/Wires 0 0 -nan
|
||||
Mode:shift_register 1.134e-07 0.0004199 0.7826
|
||||
Interc: 0 0 -nan
|
||||
ble_shift 1.134e-07 0.0004199 0.7826 Transistor Auto-Size
|
||||
Bufs/Wires 1.134e-07 0.0004199 0.7826
|
||||
Interc: 0 0 -nan
|
||||
direct3 0 0 -nan
|
||||
ff 0 0 -nan Transistor Auto-Size
|
||||
Bufs/Wires 0 0 -nan
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
#!usr/bin/perl -w
|
||||
use strict;
|
||||
use Cwd;
|
||||
#use Shell;
|
||||
use FileHandle;
|
||||
#Use the time
|
||||
use Time::gmtime;
|
||||
|
||||
my $arch_file;
|
||||
my $new_arch_file;
|
||||
my $overwrite = "TRUE";
|
||||
my $keyword = "OPENFPGAPATHKEYWORD";
|
||||
my $folder_top = "OpenFPGA";
|
||||
|
||||
sub print_usage()
|
||||
{
|
||||
print "Usage:\n";
|
||||
print " perl <script_name.pl> [-options]\n";
|
||||
print " Options:(Mandatory!)\n";
|
||||
print " -i <input_architecture_file_path>\n";
|
||||
print " Options:(Optional)\n";
|
||||
print " -o <output_architecture_file_path>\n";
|
||||
print "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
sub opts_read()
|
||||
{
|
||||
if ($#ARGV == -1){
|
||||
print "Error: Not enough input argument!\n";
|
||||
&print_usage();
|
||||
exit(1);
|
||||
} else {
|
||||
for (my $iargv = 0; $iargv < $#ARGV+1; $iargv++){
|
||||
if ("-i" eq $ARGV[$iargv]){
|
||||
$arch_file = $ARGV[$iargv+1];
|
||||
$iargv++;
|
||||
} elsif ("-o" eq $ARGV[$iargv]){
|
||||
$new_arch_file = $ARGV[$iargv+1];
|
||||
$overwrite = "FALSE";
|
||||
$iargv++;
|
||||
} else {
|
||||
die "WRONG ARGUMENT";
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub rewriting_required_check($)
|
||||
{
|
||||
my ($arch) = @_;
|
||||
open(F, $arch);
|
||||
my @lines=<F>;
|
||||
close F;
|
||||
my $grep_result = grep ($keyword, @lines);
|
||||
if($grep_result >= 1){
|
||||
print "Rewrite needed\n";
|
||||
return 1;
|
||||
} else {
|
||||
print "Rewrite NOT needed\n";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
sub save_original($)
|
||||
{
|
||||
my ($template) = @_;
|
||||
my $renamed_template = "$template".".bak";
|
||||
rename($template, $renamed_template);
|
||||
|
||||
return $renamed_template;
|
||||
}
|
||||
|
||||
sub findPath(){
|
||||
my $path;
|
||||
my $dir = cwd;
|
||||
my @folders = split("/", $dir);
|
||||
for(my $count = 0; $count < ($#folders -1); $count++){
|
||||
print "path fragment = $folders[$count]\n";
|
||||
if($folders[$count] eq ""){
|
||||
} else {
|
||||
$path = "$path"."/"."$folders[$count]";
|
||||
if($folders[$count] eq $folder_top){
|
||||
print "$path\n";
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
die "ERROR: Script launched from the outside of the $folder_top folder!\n";
|
||||
}
|
||||
|
||||
sub rewrite_file($ $)
|
||||
{
|
||||
my ($arch, $template) = @_;
|
||||
my $myPath = &findPath();
|
||||
open(IN, '<'.$template);
|
||||
open(OUT, '>'.$arch);
|
||||
while(<IN>){
|
||||
$_ =~ s/$keyword/$myPath/g;
|
||||
print OUT $_;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub main()
|
||||
{
|
||||
&opts_read();
|
||||
my $rewrite_needed = &rewriting_required_check($arch_file);
|
||||
if($rewrite_needed == 1){
|
||||
if($overwrite eq "true"){
|
||||
my $template_file = &save_original($arch_file);
|
||||
&rewrite_file($arch_file, $template_file);
|
||||
} else {
|
||||
&rewrite_file($new_arch_file, $arch_file);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
&main();
|
||||
exit(1);
|
|
@ -6,8 +6,12 @@ use FileHandle;
|
|||
#Use the time
|
||||
use Time::gmtime;
|
||||
|
||||
my $my_file;
|
||||
my $arch_file;
|
||||
my $new_arch_file;
|
||||
my $overwrite = "TRUE";
|
||||
my $keyword = "OPENFPGAPATHKEYWORD";
|
||||
my $default_keyword = "TRUE";
|
||||
my $change_to;
|
||||
my $folder_top = "OpenFPGA";
|
||||
|
||||
sub print_usage()
|
||||
|
@ -15,7 +19,10 @@ sub print_usage()
|
|||
print "Usage:\n";
|
||||
print " perl <script_name.pl> [-options]\n";
|
||||
print " Options:(Mandatory!)\n";
|
||||
print " -i <input_file_path>\n";
|
||||
print " -i <input_architecture_file_path>\n";
|
||||
print " Options:(Optional)\n";
|
||||
print " -o <output_architecture_file_path>\n";
|
||||
print " -k <keyword> <new_value>\n";
|
||||
print "\n";
|
||||
return;
|
||||
}
|
||||
|
@ -29,7 +36,17 @@ sub opts_read()
|
|||
} else {
|
||||
for (my $iargv = 0; $iargv < $#ARGV+1; $iargv++){
|
||||
if ("-i" eq $ARGV[$iargv]){
|
||||
$my_file = $ARGV[$iargv+1];
|
||||
$arch_file = $ARGV[$iargv+1];
|
||||
$iargv++;
|
||||
} elsif ("-o" eq $ARGV[$iargv]){
|
||||
$new_arch_file = $ARGV[$iargv+1];
|
||||
$overwrite = "FALSE";
|
||||
$iargv++;
|
||||
} elsif ("-k" eq $ARGV[$iargv]){
|
||||
$keyword = $ARGV[$iargv+1];
|
||||
$change_to = $ARGV[$iargv+2];
|
||||
$default_keyword = "FALSE";
|
||||
$iargv++;
|
||||
$iargv++;
|
||||
} else {
|
||||
die "WRONG ARGUMENT";
|
||||
|
@ -41,8 +58,8 @@ sub opts_read()
|
|||
|
||||
sub rewriting_required_check($)
|
||||
{
|
||||
my ($file) = @_;
|
||||
open(F, $file);
|
||||
my ($arch) = @_;
|
||||
open(F, $arch);
|
||||
my @lines=<F>;
|
||||
close F;
|
||||
my $grep_result = grep ($keyword, @lines);
|
||||
|
@ -68,7 +85,7 @@ sub findPath(){
|
|||
my $path;
|
||||
my $dir = cwd;
|
||||
my @folders = split("/", $dir);
|
||||
for(my $count = 0; $count < $#folders; $count++){
|
||||
for(my $count = 0; $count < ($#folders -1); $count++){
|
||||
if($folders[$count] eq ""){
|
||||
} else {
|
||||
$path = "$path"."/"."$folders[$count]";
|
||||
|
@ -81,28 +98,41 @@ sub findPath(){
|
|||
die "ERROR: Script launched from the outside of the $folder_top folder!\n";
|
||||
}
|
||||
|
||||
sub create_new($ $)
|
||||
sub rewrite_file($ $)
|
||||
{
|
||||
my ($file, $template) = @_;
|
||||
my $myPath = &findPath();
|
||||
my ($arch, $template) = @_;
|
||||
open(IN, '<'.$template);
|
||||
open(OUT, '>'.$file);
|
||||
open(OUT, '>'.$arch);
|
||||
|
||||
if($default_keyword eq "TRUE"){
|
||||
my $myPath = &findPath();
|
||||
while(<IN>){
|
||||
$_ =~ s/$keyword/$myPath/g;
|
||||
print OUT $_;
|
||||
}
|
||||
} else {
|
||||
while(<IN>){
|
||||
$_ =~ s/$keyword/$change_to/g;
|
||||
print OUT $_;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub main()
|
||||
{
|
||||
&opts_read();
|
||||
my $rewrite_needed = &rewriting_required_check($my_file);
|
||||
my $rewrite_needed = &rewriting_required_check($arch_file);
|
||||
if($rewrite_needed == 1){
|
||||
my $template_file = &save_original($my_file);
|
||||
&create_new($my_file, $template_file);
|
||||
if($overwrite eq "TRUE"){
|
||||
my $template_file = &save_original($arch_file);
|
||||
&rewrite_file($arch_file, $template_file);
|
||||
} else {
|
||||
&rewrite_file($new_arch_file, $arch_file);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
&main();
|
||||
exit(1);
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#VTR developed libraries
|
||||
#add_subdirectory(libarchfpga)
|
||||
add_subdirectory(libvtrutil)
|
||||
add_subdirectory(liblog)
|
||||
#add_subdirectory(libpugiutil)
|
||||
#add_subdirectory(libeasygl)
|
||||
#add_subdirectory(librtlnumber)
|
||||
|
||||
#Externally developed libraries
|
||||
#add_subdirectory(EXTERNAL)
|
|
@ -0,0 +1,24 @@
|
|||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
project("liblog")
|
||||
|
||||
file(GLOB_RECURSE EXEC_SOURCES src/main.cpp)
|
||||
file(GLOB_RECURSE LIB_SOURCES src/*.cpp)
|
||||
file(GLOB_RECURSE LIB_HEADERS src/*.h)
|
||||
files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS)
|
||||
|
||||
#Remove test executable from library
|
||||
list(REMOVE_ITEM LIB_SOURCES ${EXEC_SOURCES})
|
||||
|
||||
#Create the library
|
||||
add_library(liblog STATIC
|
||||
${LIB_HEADERS}
|
||||
${LIB_SOURCES})
|
||||
target_include_directories(liblog PUBLIC ${LIB_INCLUDE_DIRS})
|
||||
set_target_properties(liblog PROPERTIES PREFIX "") #Avoid extra 'lib' prefix
|
||||
|
||||
#Create the test executable
|
||||
add_executable(test_log ${EXEC_SOURCES})
|
||||
target_link_libraries(test_log liblog)
|
||||
|
||||
install(TARGETS test_log liblog DESTINATION bin)
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Jason Luu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,13 @@
|
|||
Simple Logger Library
|
||||
|
||||
Author: Jason Luu
|
||||
Date: Sept 5, 2014
|
||||
|
||||
This library provides simple logging operations.
|
||||
|
||||
- Output messages to both file and terminal
|
||||
- Tag messages based on feedback type (eg. info, warning, or error)
|
||||
- Track number of warnings and errors
|
||||
|
||||
Specialized programming knowledge:
|
||||
- Requires usage of variable arguments in <stdarg.h> standard C library to properly wrap printf and fprintf
|
|
@ -0,0 +1,121 @@
|
|||
/**
|
||||
* Lightweight logging tool. Automatically prepend messages with prefixes and store in log file.
|
||||
*
|
||||
* Author: Jason Luu
|
||||
* Date: Sept 5, 2014
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h> /* Allows for variable arguments, necessary for wrapping printf */
|
||||
#include "log.h"
|
||||
|
||||
#define LOG_DEFAULT_FILE_NAME "output.log"
|
||||
|
||||
static int log_warning = 0;
|
||||
static int log_error = 0;
|
||||
FILE *log_stream = nullptr;
|
||||
|
||||
|
||||
static void check_init();
|
||||
|
||||
/* Set the output file of logger.
|
||||
If different than current log file, close current log file and reopen to new log file
|
||||
*/
|
||||
void log_set_output_file(const char *filename) {
|
||||
if(log_stream != nullptr) {
|
||||
fclose(log_stream);
|
||||
}
|
||||
|
||||
if (filename == nullptr) {
|
||||
log_stream = nullptr;
|
||||
} else {
|
||||
|
||||
log_stream = fopen(filename, "w");
|
||||
if(log_stream == nullptr) {
|
||||
printf("Error writing to file %s\n\n", filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void log_print_direct(const char* message, ...) {
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
vprintf(message, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void log_print_info(const char* message, ...) {
|
||||
check_init(); /* Check if output log file setup, if not, then this function also sets it up */
|
||||
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
vprintf(message, args);
|
||||
va_end(args);
|
||||
|
||||
if (log_stream) {
|
||||
va_start(args, message); /* Must reset variable arguments so that they can be read again */
|
||||
vfprintf(log_stream, message, args);
|
||||
va_end(args);
|
||||
|
||||
fflush(log_stream);
|
||||
}
|
||||
}
|
||||
|
||||
void log_print_warning(const char* /*filename*/, unsigned int /*line_num*/, const char* message, ...) {
|
||||
check_init(); /* Check if output log file setup, if not, then this function also sets it up */
|
||||
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
log_warning++;
|
||||
|
||||
printf("Warning %d: ", log_warning);
|
||||
vprintf(message, args);
|
||||
va_end(args);
|
||||
|
||||
if (log_stream) {
|
||||
va_start(args, message); /* Must reset variable arguments so that they can be read again */
|
||||
fprintf(log_stream, "Warning %d: ", log_warning);
|
||||
vfprintf(log_stream, message, args);
|
||||
|
||||
va_end(args);
|
||||
fflush(log_stream);
|
||||
}
|
||||
}
|
||||
|
||||
void log_print_error(const char* /*filename*/, unsigned int /*line_num*/, const char* message, ...) {
|
||||
check_init(); /* Check if output log file setup, if not, then this function also sets it up */
|
||||
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
log_error++;
|
||||
|
||||
check_init();
|
||||
fprintf(stderr, "Error %d: ", log_error);
|
||||
vfprintf(stderr, message, args);
|
||||
va_end(args);
|
||||
|
||||
if (log_stream) {
|
||||
va_start(args, message); /* Must reset variable arguments so that they can be read again */
|
||||
fprintf(log_stream, "Error %d: ", log_error);
|
||||
vfprintf(log_stream, message, args);
|
||||
|
||||
va_end(args);
|
||||
|
||||
fflush(log_stream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if output log file setup, if not, then this function also sets it up
|
||||
*/
|
||||
static void check_init() {
|
||||
//We now allow a nullptr log_stream (i.e. no log file) so nothing to do here
|
||||
}
|
||||
|
||||
|
||||
void log_close() {
|
||||
if (log_stream) {
|
||||
fclose(log_stream);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* Lightweight logging tool. Automatically prepend messages with prefixes and store in log file.
|
||||
*
|
||||
* Init/Change name of log file using log_set_output_file, when done, call log_close
|
||||
*
|
||||
* Author: Jason Luu
|
||||
* Date: Sept 5, 2014
|
||||
*/
|
||||
|
||||
#ifndef LOG_H
|
||||
#define LOG_H
|
||||
|
||||
void log_set_output_file(const char *filename);
|
||||
|
||||
void log_print_direct(const char* message, ...);
|
||||
void log_print_info(const char* message, ...);
|
||||
void log_print_warning(const char* filename, unsigned int line_num, const char* message, ...);
|
||||
void log_print_error(const char* filename, unsigned int line_num, const char* message, ...);
|
||||
|
||||
void log_close();
|
||||
|
||||
#endif
|
|
@ -0,0 +1,18 @@
|
|||
/** Jason Luu
|
||||
* Test program for logger
|
||||
*/
|
||||
|
||||
#include "log.h"
|
||||
|
||||
int main() {
|
||||
int x = 10, y = 20;
|
||||
float a = 1.5f, b = -2.01f;
|
||||
log_print_info("Testing logger\n\n");
|
||||
log_print_info("Output separate strings: %s %s\n", "pass", "[PASS]");
|
||||
log_print_info("Output two integers: x = %d y = %d\n", x, y);
|
||||
log_print_warning(__FILE__, __LINE__, "Test warning on floating point arguments %g %g\n", a, b);
|
||||
log_print_error(__FILE__, __LINE__, "Test error on two variables %g %g \n\n", a - x, b + y);
|
||||
|
||||
log_print_info("Test complete\n");
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
project("libvtrutil")
|
||||
|
||||
#Version info
|
||||
set(VTR_VERSION_FILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/src/vtr_version.cpp.in)
|
||||
set(VTR_VERSION_FILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/vtr_version.cpp)
|
||||
|
||||
#Compiler info
|
||||
set(VTR_COMPILER_INFO "${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} on ${CMAKE_SYSTEM} ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
|
||||
#Set default version numbers in case not specified
|
||||
if(NOT DEFINED VTR_VERSION_MAJOR)
|
||||
set(VTR_VERSION_MAJOR 0)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED VTR_VERSION_MINOR)
|
||||
set(VTR_VERSION_MINOR 0)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED VTR_VERSION_PATCH)
|
||||
set(VTR_VERSION_PATCH 0)
|
||||
endif()
|
||||
|
||||
# We always update the vtr_version.cpp file every time the project is built,
|
||||
# to ensure the git revision and dirty status are up to date.
|
||||
#
|
||||
# We need to do this in two stages:
|
||||
#
|
||||
# 1) We a custom target 'version' (which is always out of date) so it will always be run.
|
||||
# It touches the unprocessed version input file so it too will always be out of date.
|
||||
#
|
||||
# 2) The custom command depends on the touched version input file and generates the processed
|
||||
# version file, with updated values. The custom command uses the configure_version.cmake
|
||||
# script to generate the up-to-date vtr_version.cpp
|
||||
add_custom_target(version ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E touch ${VTR_VERSION_FILE_IN})
|
||||
|
||||
add_custom_command(OUTPUT ${VTR_VERSION_FILE_OUT}
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-D IN_FILE=${VTR_VERSION_FILE_IN}
|
||||
-D OUT_FILE=${VTR_VERSION_FILE_OUT}
|
||||
-D VTR_VERSION_MAJOR=${VTR_VERSION_MAJOR}
|
||||
-D VTR_VERSION_MINOR=${VTR_VERSION_MINOR}
|
||||
-D VTR_VERSION_PATCH=${VTR_VERSION_PATCH}
|
||||
-D VTR_VERSION_PRERELEASE=${VTR_VERSION_PRERELEASE}
|
||||
-D VTR_COMPILER_INFO=${VTR_COMPILER_INFO}
|
||||
-D VTR_BUILD_TYPE=${CMAKE_BUILD_TYPE}
|
||||
-P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/configure_version.cmake
|
||||
MAIN_DEPENDENCY ${VTR_VERSION_FILE_IN}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
VERBATIM)
|
||||
|
||||
|
||||
#
|
||||
# Source files and library
|
||||
#
|
||||
file(GLOB_RECURSE LIB_SOURCES src/*.cpp)
|
||||
file(GLOB_RECURSE LIB_HEADERS src/*.hpp src/*.h)
|
||||
files_to_dirs(LIB_HEADERS LIB_INCLUDE_DIRS)
|
||||
|
||||
#Add the version file to the sources
|
||||
list(APPEND LIB_SOURCES ${VTR_VERSION_FILE_OUT})
|
||||
|
||||
#Create the library
|
||||
add_library(libvtrutil STATIC
|
||||
${LIB_HEADERS}
|
||||
${LIB_SOURCES})
|
||||
target_include_directories(libvtrutil PUBLIC ${LIB_INCLUDE_DIRS})
|
||||
set_target_properties(libvtrutil PROPERTIES PREFIX "") #Avoid extra 'lib' prefix
|
||||
|
||||
#Ensure version is always up to date by requiring version to be run first
|
||||
add_dependencies(libvtrutil version)
|
||||
|
||||
#Specify link-time dependancies
|
||||
target_link_libraries(libvtrutil
|
||||
liblog)
|
||||
|
||||
install(TARGETS libvtrutil DESTINATION bin)
|
||||
|
||||
#
|
||||
# Unit Tests
|
||||
#
|
||||
#file(GLOB_RECURSE TEST_SOURCES test/*.cpp)
|
||||
#add_executable(test_vtrutil ${TEST_SOURCES})
|
||||
#target_link_libraries(test_vtrutil
|
||||
# libvtrutil
|
||||
# libcatch)
|
||||
|
||||
#add_test(NAME test_vtrutil COMMAND test_vtrutil --use-colour=yes)
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#
|
||||
# Versioning information
|
||||
#
|
||||
#Figure out the git revision
|
||||
find_package(Git QUIET)
|
||||
if(GIT_FOUND)
|
||||
exec_program(${GIT_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
ARGS describe --always --long --dirty
|
||||
OUTPUT_VARIABLE VTR_VCS_REVISION
|
||||
RETURN_VALUE GIT_DESCRIBE_RETURN_VALUE)
|
||||
|
||||
if(NOT GIT_DESCRIBE_RETURN_VALUE EQUAL 0)
|
||||
#Git describe failed, usually this means we
|
||||
#aren't in a git repo -- so don't set a VCS
|
||||
#revision
|
||||
set(VTR_VCS_REVISION "unkown")
|
||||
endif()
|
||||
else()
|
||||
#Couldn't find git, so can't look-up VCS revision
|
||||
set(VTR_VCS_REVISION "unkown")
|
||||
endif()
|
||||
|
||||
|
||||
#Set the version according to semver.org
|
||||
set(VTR_VERSION "${VTR_VERSION_MAJOR}.${VTR_VERSION_MINOR}.${VTR_VERSION_PATCH}")
|
||||
if(VTR_VERSION_PRERELEASE)
|
||||
set(VTR_VERSION "${VTR_VERSION}-${VTR_VERSION_PRERELEASE}")
|
||||
endif()
|
||||
set(VTR_VERSION_SHORT ${VTR_VERSION})
|
||||
if(VTR_VCS_REVISION)
|
||||
set(VTR_VERSION "${VTR_VERSION}+${VTR_VCS_REVISION}")
|
||||
endif()
|
||||
|
||||
#Other build meta-data
|
||||
string(TIMESTAMP VTR_BUILD_TIMESTAMP)
|
||||
set(VTR_BUILD_TIMESTAMP "${VTR_BUILD_TIMESTAMP} (${VTR_BUILD_TYPE} build)")
|
||||
|
||||
message(STATUS "VTR Version: ${VTR_VERSION}")
|
||||
|
||||
configure_file(${IN_FILE} ${OUT_FILE})
|
|
@ -0,0 +1,366 @@
|
|||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (C) 2014 okdshin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
#ifndef PICOSHA2_H
|
||||
#define PICOSHA2_H
|
||||
//picosha2:20140213
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
namespace picosha2
|
||||
{
|
||||
typedef unsigned long word_t;
|
||||
typedef unsigned char byte_t;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
inline byte_t mask_8bit(byte_t x){
|
||||
return x&0xff;
|
||||
}
|
||||
|
||||
inline word_t mask_32bit(word_t x){
|
||||
return x&0xffffffff;
|
||||
}
|
||||
|
||||
const word_t add_constant[64] = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
||||
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
||||
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
||||
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
||||
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
|
||||
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
|
||||
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||
};
|
||||
|
||||
const word_t initial_message_digest[8] = {
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
|
||||
};
|
||||
|
||||
inline word_t ch(word_t x, word_t y, word_t z){
|
||||
return (x&y)^((~x)&z);
|
||||
}
|
||||
|
||||
inline word_t maj(word_t x, word_t y, word_t z){
|
||||
return (x&y)^(x&z)^(y&z);
|
||||
}
|
||||
|
||||
inline word_t rotr(word_t x, std::size_t n){
|
||||
assert(n < 32);
|
||||
return mask_32bit((x>>n)|(x<<(32-n)));
|
||||
}
|
||||
|
||||
inline word_t bsig0(word_t x){
|
||||
return rotr(x, 2)^rotr(x, 13)^rotr(x, 22);
|
||||
}
|
||||
|
||||
inline word_t bsig1(word_t x){
|
||||
return rotr(x, 6)^rotr(x, 11)^rotr(x, 25);
|
||||
}
|
||||
|
||||
inline word_t shr(word_t x, std::size_t n){
|
||||
assert(n < 32);
|
||||
return x >> n;
|
||||
}
|
||||
|
||||
inline word_t ssig0(word_t x){
|
||||
return rotr(x, 7)^rotr(x, 18)^shr(x, 3);
|
||||
}
|
||||
|
||||
inline word_t ssig1(word_t x){
|
||||
return rotr(x, 17)^rotr(x, 19)^shr(x, 10);
|
||||
}
|
||||
|
||||
template<typename RaIter1, typename RaIter2>
|
||||
void hash256_block(RaIter1 message_digest, RaIter2 first, RaIter2 /*last*/){
|
||||
word_t w[64];
|
||||
std::fill(w, w+64, 0);
|
||||
for(std::size_t i = 0; i < 16; ++i){
|
||||
w[i] = (static_cast<word_t>(mask_8bit(*(first+i*4)))<<24)
|
||||
|(static_cast<word_t>(mask_8bit(*(first+i*4+1)))<<16)
|
||||
|(static_cast<word_t>(mask_8bit(*(first+i*4+2)))<<8)
|
||||
|(static_cast<word_t>(mask_8bit(*(first+i*4+3))));
|
||||
}
|
||||
for(std::size_t i = 16; i < 64; ++i){
|
||||
w[i] = mask_32bit(ssig1(w[i-2])+w[i-7]+ssig0(w[i-15])+w[i-16]);
|
||||
}
|
||||
|
||||
word_t a = *message_digest;
|
||||
word_t b = *(message_digest+1);
|
||||
word_t c = *(message_digest+2);
|
||||
word_t d = *(message_digest+3);
|
||||
word_t e = *(message_digest+4);
|
||||
word_t f = *(message_digest+5);
|
||||
word_t g = *(message_digest+6);
|
||||
word_t h = *(message_digest+7);
|
||||
|
||||
for(std::size_t i = 0; i < 64; ++i){
|
||||
word_t temp1 = h+bsig1(e)+ch(e,f,g)+add_constant[i]+w[i];
|
||||
word_t temp2 = bsig0(a)+maj(a,b,c);
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = mask_32bit(d+temp1);
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = mask_32bit(temp1+temp2);
|
||||
}
|
||||
*message_digest += a;
|
||||
*(message_digest+1) += b;
|
||||
*(message_digest+2) += c;
|
||||
*(message_digest+3) += d;
|
||||
*(message_digest+4) += e;
|
||||
*(message_digest+5) += f;
|
||||
*(message_digest+6) += g;
|
||||
*(message_digest+7) += h;
|
||||
for(std::size_t i = 0; i < 8; ++i){
|
||||
*(message_digest+i) = mask_32bit(*(message_digest+i));
|
||||
}
|
||||
}
|
||||
|
||||
}//namespace detail
|
||||
|
||||
template<typename InIter>
|
||||
void output_hex(InIter first, InIter last, std::ostream& os){
|
||||
std::ios::fmtflags orig_flags = os.flags();
|
||||
std::streamsize orig_width = os.width();
|
||||
char orig_fill = os.fill();
|
||||
|
||||
|
||||
os.setf(std::ios::hex, std::ios::basefield);
|
||||
while(first != last){
|
||||
os.width(2);
|
||||
os.fill('0');
|
||||
os << static_cast<unsigned int>(*first);
|
||||
++first;
|
||||
}
|
||||
os.flags(orig_flags);
|
||||
os.fill(orig_fill);
|
||||
os.width(orig_width);
|
||||
}
|
||||
|
||||
template<typename InIter>
|
||||
void bytes_to_hex_string(InIter first, InIter last, std::string& hex_str){
|
||||
std::ostringstream oss;
|
||||
output_hex(first, last, oss);
|
||||
hex_str.assign(oss.str());
|
||||
}
|
||||
|
||||
template<typename InContainer>
|
||||
void bytes_to_hex_string(const InContainer& bytes, std::string& hex_str){
|
||||
bytes_to_hex_string(bytes.begin(), bytes.end(), hex_str);
|
||||
}
|
||||
|
||||
template<typename InIter>
|
||||
std::string bytes_to_hex_string(InIter first, InIter last){
|
||||
std::string hex_str;
|
||||
bytes_to_hex_string(first, last, hex_str);
|
||||
return hex_str;
|
||||
}
|
||||
|
||||
template<typename InContainer>
|
||||
std::string bytes_to_hex_string(const InContainer& bytes){
|
||||
std::string hex_str;
|
||||
bytes_to_hex_string(bytes, hex_str);
|
||||
return hex_str;
|
||||
}
|
||||
|
||||
class hash256_one_by_one {
|
||||
public:
|
||||
hash256_one_by_one(){
|
||||
init();
|
||||
}
|
||||
|
||||
void init(){
|
||||
buffer_.clear();
|
||||
std::fill(data_length_digits_, data_length_digits_+4, 0);
|
||||
std::copy(detail::initial_message_digest, detail::initial_message_digest+8, h_);
|
||||
}
|
||||
|
||||
template<typename RaIter>
|
||||
void process(RaIter first, RaIter last){
|
||||
add_to_data_length(std::distance(first, last));
|
||||
std::copy(first, last, std::back_inserter(buffer_));
|
||||
std::size_t i = 0;
|
||||
for(;i+64 <= buffer_.size(); i+=64){
|
||||
detail::hash256_block(h_, buffer_.begin()+i, buffer_.begin()+i+64);
|
||||
}
|
||||
buffer_.erase(buffer_.begin(), buffer_.begin()+i);
|
||||
}
|
||||
|
||||
void finish(){
|
||||
byte_t temp[64];
|
||||
std::fill(temp, temp+64, 0);
|
||||
std::size_t remains = buffer_.size();
|
||||
std::copy(buffer_.begin(), buffer_.end(), temp);
|
||||
temp[remains] = 0x80;
|
||||
|
||||
if(remains > 55){
|
||||
std::fill(temp+remains+1, temp+64, 0);
|
||||
detail::hash256_block(h_, temp, temp+64);
|
||||
std::fill(temp, temp+64-4, 0);
|
||||
}
|
||||
else {
|
||||
std::fill(temp+remains+1, temp+64-4, 0);
|
||||
}
|
||||
|
||||
write_data_bit_length(&(temp[56]));
|
||||
detail::hash256_block(h_, temp, temp+64);
|
||||
}
|
||||
|
||||
template<typename OutIter>
|
||||
void get_hash_bytes(OutIter first, OutIter last)const{
|
||||
for(const word_t* iter = h_; iter != h_+8; ++iter){
|
||||
for(std::size_t i = 0; i < 4 && first != last; ++i){
|
||||
*(first++) = detail::mask_8bit(static_cast<byte_t>((*iter >> (24-8*i))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void add_to_data_length(word_t n) {
|
||||
word_t carry = 0;
|
||||
data_length_digits_[0] += n;
|
||||
for(std::size_t i = 0; i < 4; ++i) {
|
||||
data_length_digits_[i] += carry;
|
||||
if(data_length_digits_[i] >= 65536u) {
|
||||
carry = data_length_digits_[i]>>16;
|
||||
data_length_digits_[i] &= 65535u;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void write_data_bit_length(byte_t* begin) {
|
||||
word_t data_bit_length_digits[4];
|
||||
std::copy(
|
||||
data_length_digits_, data_length_digits_+4,
|
||||
data_bit_length_digits
|
||||
);
|
||||
|
||||
// convert byte length to bit length (multiply 8 or shift 3 times left)
|
||||
word_t carry = 0;
|
||||
for(std::size_t i = 0; i < 4; ++i) {
|
||||
word_t before_val = data_bit_length_digits[i];
|
||||
data_bit_length_digits[i] <<= 3;
|
||||
data_bit_length_digits[i] |= carry;
|
||||
data_bit_length_digits[i] &= 65535u;
|
||||
carry = (before_val >> (16-3)) & 65535u;
|
||||
}
|
||||
|
||||
// write data_bit_length
|
||||
for(int i = 3; i >= 0; --i) {
|
||||
(*begin++) = static_cast<byte_t>(data_bit_length_digits[i] >> 8);
|
||||
(*begin++) = static_cast<byte_t>(data_bit_length_digits[i]);
|
||||
}
|
||||
}
|
||||
std::vector<byte_t> buffer_;
|
||||
word_t data_length_digits_[4]; //as 64bit integer (16bit x 4 integer)
|
||||
word_t h_[8];
|
||||
};
|
||||
|
||||
inline void get_hash_hex_string(const hash256_one_by_one& hasher, std::string& hex_str){
|
||||
byte_t hash[32];
|
||||
hasher.get_hash_bytes(hash, hash+32);
|
||||
return bytes_to_hex_string(hash, hash+32, hex_str);
|
||||
}
|
||||
|
||||
inline std::string get_hash_hex_string(const hash256_one_by_one& hasher){
|
||||
std::string hex_str;
|
||||
get_hash_hex_string(hasher, hex_str);
|
||||
return hex_str;
|
||||
}
|
||||
|
||||
template<typename RaIter, typename OutIter>
|
||||
void hash256(RaIter first, RaIter last, OutIter first2, OutIter last2){
|
||||
hash256_one_by_one hasher;
|
||||
//hasher.init();
|
||||
hasher.process(first, last);
|
||||
hasher.finish();
|
||||
hasher.get_hash_bytes(first2, last2);
|
||||
}
|
||||
|
||||
template<typename RaIter, typename OutContainer>
|
||||
void hash256(RaIter first, RaIter last, OutContainer& dst){
|
||||
hash256(first, last, dst.begin(), dst.end());
|
||||
}
|
||||
|
||||
template<typename RaContainer, typename OutIter>
|
||||
void hash256(const RaContainer& src, OutIter first, OutIter last){
|
||||
hash256(src.begin(), src.end(), first, last);
|
||||
}
|
||||
|
||||
template<typename RaContainer, typename OutContainer>
|
||||
void hash256(const RaContainer& src, OutContainer& dst){
|
||||
hash256(src.begin(), src.end(), dst.begin(), dst.end());
|
||||
}
|
||||
|
||||
|
||||
template<typename RaIter>
|
||||
void hash256_hex_string(RaIter first, RaIter last, std::string& hex_str){
|
||||
byte_t hashed[32];
|
||||
hash256(first, last, hashed, hashed+32);
|
||||
std::ostringstream oss;
|
||||
output_hex(hashed, hashed+32, oss);
|
||||
hex_str.assign(oss.str());
|
||||
}
|
||||
|
||||
template<typename RaIter>
|
||||
std::string hash256_hex_string(RaIter first, RaIter last){
|
||||
std::string hex_str;
|
||||
hash256_hex_string(first, last, hex_str);
|
||||
return hex_str;
|
||||
}
|
||||
|
||||
inline void hash256_hex_string(const std::string& src, std::string& hex_str){
|
||||
hash256_hex_string(src.begin(), src.end(), hex_str);
|
||||
}
|
||||
|
||||
template<typename RaContainer>
|
||||
void hash256_hex_string(const RaContainer& src, std::string& hex_str){
|
||||
hash256_hex_string(src.begin(), src.end(), hex_str);
|
||||
}
|
||||
|
||||
template<typename RaContainer>
|
||||
std::string hash256_hex_string(const RaContainer& src){
|
||||
return hash256_hex_string(src.begin(), src.end());
|
||||
}
|
||||
|
||||
}//namespace picosha2
|
||||
|
||||
#endif //PICOSHA2_H
|
|
@ -0,0 +1,21 @@
|
|||
#include "vtr_assert.h"
|
||||
|
||||
#include <cstdio> //fprintf, stderr
|
||||
#include <cstdlib> //abort
|
||||
|
||||
namespace vtr { namespace assert {
|
||||
|
||||
void handle_assert(const char* expr, const char* file, unsigned int line, const char* function, const char* msg) {
|
||||
fprintf(stderr, "%s:%d", file, line);
|
||||
if(function) {
|
||||
fprintf(stderr, " %s:", function);
|
||||
}
|
||||
fprintf(stderr, " Assertion '%s' failed", expr);
|
||||
if(msg) {
|
||||
fprintf(stderr, " (%s)", msg);
|
||||
}
|
||||
fprintf(stderr, ".\n");
|
||||
std::abort();
|
||||
}
|
||||
|
||||
}} //namespace
|
|
@ -0,0 +1,133 @@
|
|||
#ifndef VTR_ASSERT_H
|
||||
#define VTR_ASSERT_H
|
||||
/*
|
||||
* The header defines useful assertion macros for VTR projects.
|
||||
*
|
||||
* Three types of assertions are defined:
|
||||
* VTR_ASSERT_OPT - low overhead assertions that should always be enabled
|
||||
* VTR_ASSERT - medium overhead assertions that are usually be enabled
|
||||
* VTR_ASSERT_SAFE - high overhead assertions typically enabled only for debugging
|
||||
* VTR_ASSERT_DEBUG - very high overhead assertions typically enabled only for extreme debugging
|
||||
* Each of the above assertions also have a *_MSG variants (e.g. VTR_ASSERT_MSG(expr, msg))
|
||||
* which takes an additional argument specifying additional message text to be shown.
|
||||
* By convention the message should state the condition *being checked* (and not the failure condition),
|
||||
* since that the condition failed is obvious from the assertion failure itself.
|
||||
*
|
||||
* The macro VTR_ASSERT_LEVEL specifies the level of assertion checking desired:
|
||||
*
|
||||
* VTR_ASSERT_LEVEL == 4: VTR_ASSERT_OPT, VTR_ASSERT, VTR_ASSERT_SAFE, VTR_ASSERT_DEBUG enabled
|
||||
* VTR_ASSERT_LEVEL == 3: VTR_ASSERT_OPT, VTR_ASSERT, VTR_ASSERT_SAFE enabled
|
||||
* VTR_ASSERT_LEVEL == 2: VTR_ASSERT_OPT, VTR_ASSERT enabled
|
||||
* VTR_ASSERT_LEVEL == 1: VTR_ASSERT_OPT enabled
|
||||
* VTR_ASSERT_LEVEL == 0: No assertion checking enabled
|
||||
* Note that an assertion levels beyond 4 are currently treated the same as level 4
|
||||
*/
|
||||
|
||||
//Set a default assertion level if none is specified
|
||||
#ifndef VTR_ASSERT_LEVEL
|
||||
# define VTR_ASSERT_LEVEL 2
|
||||
#endif
|
||||
|
||||
//Enable the assertions based on the specified level
|
||||
#if VTR_ASSERT_LEVEL >= 4
|
||||
# define VTR_ASSERT_DEBUG_ENABLED
|
||||
#endif
|
||||
|
||||
#if VTR_ASSERT_LEVEL >= 3
|
||||
# define VTR_ASSERT_SAFE_ENABLED
|
||||
#endif
|
||||
|
||||
#if VTR_ASSERT_LEVEL >= 2
|
||||
#define VTR_ASSERT_ENABLED
|
||||
#endif
|
||||
|
||||
#if VTR_ASSERT_LEVEL >= 1
|
||||
# define VTR_ASSERT_OPT_ENABLED
|
||||
#endif
|
||||
|
||||
//Define the user assertion macros
|
||||
#ifdef VTR_ASSERT_DEBUG_ENABLED
|
||||
# define VTR_ASSERT_DEBUG(expr) VTR_ASSERT_IMPL(expr, nullptr)
|
||||
# define VTR_ASSERT_DEBUG_MSG(expr, msg) VTR_ASSERT_IMPL(expr, msg)
|
||||
#else
|
||||
# define VTR_ASSERT_DEBUG(expr) VTR_ASSERT_IMPL_NOP(expr, nullptr)
|
||||
# define VTR_ASSERT_DEBUG_MSG(expr, msg) VTR_ASSERT_IMPL_NOP(expr, msg)
|
||||
#endif
|
||||
|
||||
#ifdef VTR_ASSERT_SAFE_ENABLED
|
||||
# define VTR_ASSERT_SAFE(expr) VTR_ASSERT_IMPL(expr, nullptr)
|
||||
# define VTR_ASSERT_SAFE_MSG(expr, msg) VTR_ASSERT_IMPL(expr, msg)
|
||||
#else
|
||||
# define VTR_ASSERT_SAFE(expr) VTR_ASSERT_IMPL_NOP(expr, nullptr)
|
||||
# define VTR_ASSERT_SAFE_MSG(expr, msg) VTR_ASSERT_IMPL_NOP(expr, msg)
|
||||
#endif
|
||||
|
||||
#ifdef VTR_ASSERT_ENABLED
|
||||
# define VTR_ASSERT(expr) VTR_ASSERT_IMPL(expr, nullptr)
|
||||
# define VTR_ASSERT_MSG(expr, msg) VTR_ASSERT_IMPL(expr, msg)
|
||||
#else
|
||||
# define VTR_ASSERT(expr) VTR_ASSERT_IMPL_NOP(expr, nullptr)
|
||||
# define VTR_ASSERT_MSG(expr, msg) VTR_ASSERT_IMPL_NOP(expr, msg)
|
||||
#endif
|
||||
|
||||
#ifdef VTR_ASSERT_OPT_ENABLED
|
||||
# define VTR_ASSERT_OPT(expr) VTR_ASSERT_IMPL(expr, nullptr)
|
||||
# define VTR_ASSERT_OPT_MSG(expr, msg) VTR_ASSERT_IMPL(expr, msg)
|
||||
#else
|
||||
# define VTR_ASSERT_OPT(expr) VTR_ASSERT_IMPL_NOP(expr, nullptr)
|
||||
# define VTR_ASSERT_OPT_MSG(expr, msg) VTR_ASSERT_IMPL_NOP(expr, msg)
|
||||
#endif
|
||||
|
||||
|
||||
//Define the assertion implementation macro
|
||||
// We wrap the check in a do {} while() to ensure the function-like
|
||||
// macro can be always be followed by a ';'
|
||||
#define VTR_ASSERT_IMPL(expr, msg) do { \
|
||||
if(!(expr)) { \
|
||||
vtr::assert::handle_assert(#expr, __FILE__, __LINE__, VTR_ASSERT_FUNCTION, msg); \
|
||||
} \
|
||||
} while(false)
|
||||
|
||||
//Define the no-op assertion implementation macro
|
||||
// We wrap the check in a do {} while() to ensure the function-like
|
||||
// macro can be always be followed by a ';'
|
||||
//
|
||||
// Note that to avoid 'unused' variable warnings when assertions are
|
||||
// disabled, we pass the expr and msg to sizeof(). We use sizeof specifically
|
||||
// since it accepts expressions, and the C++ standard gaurentees sizeof's arguments
|
||||
// are never evaluated (ensuring any expensive expressions are not evaluated when
|
||||
// assertions are disabled). To avoid warnings about the unused result of sizeof()
|
||||
// we cast it to void.
|
||||
#define VTR_ASSERT_IMPL_NOP(expr, msg) do { \
|
||||
static_cast<void>(sizeof(expr)); \
|
||||
static_cast<void>(sizeof(msg)); \
|
||||
} while(false)
|
||||
|
||||
//Figure out what macro to use to get the name of the current function
|
||||
// We default to __func__ which is defined in C99
|
||||
//
|
||||
// g++ > 2.6 define __PRETTY_FUNC__ which includes class/namespace/overload
|
||||
// information, so we prefer to use it if possible
|
||||
#define VTR_ASSERT_FUNCTION __func__
|
||||
#ifdef __GNUC__
|
||||
# ifdef __GNUC_MINOR__
|
||||
# if __GNUC__ >= 2 && __GNUC_MINOR__ > 6
|
||||
# undef VTR_ASSERT_FUNCTION
|
||||
# define VTR_ASSERT_FUNCTION __PRETTY_FUNCTION__
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace vtr { namespace assert {
|
||||
//Assertion handling routine
|
||||
//
|
||||
//Note that we mark the routine with the standard C++11
|
||||
//attribute 'noreturn' which tells the compiler this
|
||||
//function will never return. This should ensure the
|
||||
//compiler won't warn about detected conditions such as
|
||||
//dead-code or potential null pointer dereferences
|
||||
//which are gaurded against by assertions.
|
||||
[[noreturn]] void handle_assert(const char* expr, const char* file, unsigned int line, const char* function, const char* msg);
|
||||
}} //namespace
|
||||
|
||||
#endif //VTR_ASSERT_H
|
|
@ -0,0 +1,142 @@
|
|||
#ifndef VTR_BIMAP
|
||||
#define VTR_BIMAP
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include "vtr_flat_map.h"
|
||||
#include "vtr_linear_map.h"
|
||||
|
||||
#include "vtr_error.h"
|
||||
|
||||
namespace vtr {
|
||||
|
||||
/*
|
||||
* A map-like class which provides a bi-directonal mapping between key and value
|
||||
*
|
||||
* Keys and values can be looked up directly by passing either the key or value.
|
||||
* the indexing operator will throw if the key/value does not exist.
|
||||
*/
|
||||
template<class K, class V, template<typename ...> class Map=std::map, template<typename ...> class InvMap=std::map>
|
||||
class bimap {
|
||||
public: //Public types
|
||||
typedef typename Map<K,V>::const_iterator iterator;
|
||||
typedef typename InvMap<V,K>::const_iterator inverse_iterator;
|
||||
public: //Accessors
|
||||
|
||||
//Iterators
|
||||
iterator begin() const { return map_.begin(); }
|
||||
iterator end() const { return map_.end(); }
|
||||
inverse_iterator inverse_begin() const { return inverse_map_.begin(); }
|
||||
inverse_iterator inverse_end() const { return inverse_map_.end(); }
|
||||
|
||||
//Return an iterator to the key-value pair matching key, or end() if not found
|
||||
iterator find(const K key) const {
|
||||
return map_.find(key);
|
||||
}
|
||||
|
||||
//Return an iterator to the value-key pair matching value, or inverse_end() if not found
|
||||
inverse_iterator find(const V value) const {
|
||||
return inverse_map_.find(value);
|
||||
}
|
||||
|
||||
//Return an immutable reference to the value matching key
|
||||
//Will throw an exception if key is not found
|
||||
const V& operator[] (const K key) const {
|
||||
auto iter = find(key);
|
||||
if(iter == end()) {
|
||||
throw VtrError("Invalid bimap key during look-up", __FILE__, __LINE__);
|
||||
}
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
//Return an immutable reference to the key matching value
|
||||
//Will throw an exception if value is not found
|
||||
const K& operator[] (const V value) const {
|
||||
auto iter = find(value);
|
||||
if(iter == inverse_end()) {
|
||||
throw VtrError("Invalid bimap value during inverse look-up", __FILE__, __LINE__);
|
||||
}
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
//Return the number of key-value pairs stored
|
||||
std::size_t size() const { VTR_ASSERT(map_.size() == inverse_map_.size()); return map_.size(); }
|
||||
|
||||
//Return true if there are no key-value pairs stored
|
||||
bool empty() const { return (size() == 0); }
|
||||
|
||||
//Return true if the specified key exists
|
||||
bool contains(const K key) const { return find(key) != end(); }
|
||||
|
||||
//Return true if the specified value exists
|
||||
bool contains(const V value) const { return find(value) != inverse_end(); }
|
||||
|
||||
public: //Mutators
|
||||
|
||||
//Drop all stored key-values
|
||||
void clear() { map_.clear(); inverse_map_.clear(); }
|
||||
|
||||
//Insert a key-value pair, if not already in map
|
||||
std::pair<iterator,bool> insert(const K key, const V value) {
|
||||
auto ret1 = map_.insert({key,value});
|
||||
auto ret2 = inverse_map_.insert({value,key});
|
||||
|
||||
VTR_ASSERT(ret1.second == ret2.second);
|
||||
|
||||
//Return true if inserted
|
||||
return ret1;
|
||||
}
|
||||
|
||||
//Update a key-value pair, will insert if not already in map
|
||||
void update(const K key, const V value) {
|
||||
map_[key] = value;
|
||||
inverse_map_[value] = key;
|
||||
}
|
||||
|
||||
//Remove the specified key (and it's associated value)
|
||||
void erase(const K key) {
|
||||
auto iter = map_.find(key);
|
||||
if(iter != map_.end()) {
|
||||
V val = iter->second;
|
||||
map_.erase(iter);
|
||||
|
||||
auto inv_iter = inverse_map_.find(val);
|
||||
VTR_ASSERT(inv_iter != inverse_map_.end());
|
||||
inverse_map_.erase(inv_iter);
|
||||
}
|
||||
}
|
||||
|
||||
//Remove the specified value (and it's associated key)
|
||||
void erase(const V val) {
|
||||
auto inv_iter = inverse_map_.find(val);
|
||||
if(inv_iter != inverse_map_.end()) {
|
||||
K key = inv_iter->second;
|
||||
inverse_map_.erase(inv_iter);
|
||||
|
||||
auto iter = map_.find(key);
|
||||
VTR_ASSERT(iter != map_.end());
|
||||
map_.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
//Swap (this enables std::swap via ADL)
|
||||
friend void swap(bimap<K,V,Map,InvMap>& x, bimap<K,V,Map,InvMap>& y) {
|
||||
std::swap(x.map_, y.map_);
|
||||
std::swap(x.inverse_map_, y.inverse_map_);
|
||||
}
|
||||
private:
|
||||
Map<K,V> map_;
|
||||
InvMap<V,K> inverse_map_;
|
||||
};
|
||||
|
||||
template<class K, class V>
|
||||
using unordered_bimap = bimap<K,V,std::unordered_map,std::unordered_map>;
|
||||
|
||||
template<class K, class V>
|
||||
using flat_bimap = bimap<K,V,vtr::flat_map,vtr::flat_map>;
|
||||
|
||||
template<class K, class V>
|
||||
using linear_bimap = bimap<K,V,vtr::linear_map,vtr::linear_map>;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,831 @@
|
|||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include "vtr_color_map.h"
|
||||
#include "vtr_assert.h"
|
||||
|
||||
namespace vtr {
|
||||
|
||||
//Inferno data from MatPlotLib
|
||||
static std::vector<Color<float>> inferno_data = {
|
||||
{0.001462f, 0.000466f, 0.013866f},
|
||||
{0.002267f, 0.001270f, 0.018570f},
|
||||
{0.003299f, 0.002249f, 0.024239f},
|
||||
{0.004547f, 0.003392f, 0.030909f},
|
||||
{0.006006f, 0.004692f, 0.038558f},
|
||||
{0.007676f, 0.006136f, 0.046836f},
|
||||
{0.009561f, 0.007713f, 0.055143f},
|
||||
{0.011663f, 0.009417f, 0.063460f},
|
||||
{0.013995f, 0.011225f, 0.071862f},
|
||||
{0.016561f, 0.013136f, 0.080282f},
|
||||
{0.019373f, 0.015133f, 0.088767f},
|
||||
{0.022447f, 0.017199f, 0.097327f},
|
||||
{0.025793f, 0.019331f, 0.105930f},
|
||||
{0.029432f, 0.021503f, 0.114621f},
|
||||
{0.033385f, 0.023702f, 0.123397f},
|
||||
{0.037668f, 0.025921f, 0.132232f},
|
||||
{0.042253f, 0.028139f, 0.141141f},
|
||||
{0.046915f, 0.030324f, 0.150164f},
|
||||
{0.051644f, 0.032474f, 0.159254f},
|
||||
{0.056449f, 0.034569f, 0.168414f},
|
||||
{0.061340f, 0.036590f, 0.177642f},
|
||||
{0.066331f, 0.038504f, 0.186962f},
|
||||
{0.071429f, 0.040294f, 0.196354f},
|
||||
{0.076637f, 0.041905f, 0.205799f},
|
||||
{0.081962f, 0.043328f, 0.215289f},
|
||||
{0.087411f, 0.044556f, 0.224813f},
|
||||
{0.092990f, 0.045583f, 0.234358f},
|
||||
{0.098702f, 0.046402f, 0.243904f},
|
||||
{0.104551f, 0.047008f, 0.253430f},
|
||||
{0.110536f, 0.047399f, 0.262912f},
|
||||
{0.116656f, 0.047574f, 0.272321f},
|
||||
{0.122908f, 0.047536f, 0.281624f},
|
||||
{0.129285f, 0.047293f, 0.290788f},
|
||||
{0.135778f, 0.046856f, 0.299776f},
|
||||
{0.142378f, 0.046242f, 0.308553f},
|
||||
{0.149073f, 0.045468f, 0.317085f},
|
||||
{0.155850f, 0.044559f, 0.325338f},
|
||||
{0.162689f, 0.043554f, 0.333277f},
|
||||
{0.169575f, 0.042489f, 0.340874f},
|
||||
{0.176493f, 0.041402f, 0.348111f},
|
||||
{0.183429f, 0.040329f, 0.354971f},
|
||||
{0.190367f, 0.039309f, 0.361447f},
|
||||
{0.197297f, 0.038400f, 0.367535f},
|
||||
{0.204209f, 0.037632f, 0.373238f},
|
||||
{0.211095f, 0.037030f, 0.378563f},
|
||||
{0.217949f, 0.036615f, 0.383522f},
|
||||
{0.224763f, 0.036405f, 0.388129f},
|
||||
{0.231538f, 0.036405f, 0.392400f},
|
||||
{0.238273f, 0.036621f, 0.396353f},
|
||||
{0.244967f, 0.037055f, 0.400007f},
|
||||
{0.251620f, 0.037705f, 0.403378f},
|
||||
{0.258234f, 0.038571f, 0.406485f},
|
||||
{0.264810f, 0.039647f, 0.409345f},
|
||||
{0.271347f, 0.040922f, 0.411976f},
|
||||
{0.277850f, 0.042353f, 0.414392f},
|
||||
{0.284321f, 0.043933f, 0.416608f},
|
||||
{0.290763f, 0.045644f, 0.418637f},
|
||||
{0.297178f, 0.047470f, 0.420491f},
|
||||
{0.303568f, 0.049396f, 0.422182f},
|
||||
{0.309935f, 0.051407f, 0.423721f},
|
||||
{0.316282f, 0.053490f, 0.425116f},
|
||||
{0.322610f, 0.055634f, 0.426377f},
|
||||
{0.328921f, 0.057827f, 0.427511f},
|
||||
{0.335217f, 0.060060f, 0.428524f},
|
||||
{0.341500f, 0.062325f, 0.429425f},
|
||||
{0.347771f, 0.064616f, 0.430217f},
|
||||
{0.354032f, 0.066925f, 0.430906f},
|
||||
{0.360284f, 0.069247f, 0.431497f},
|
||||
{0.366529f, 0.071579f, 0.431994f},
|
||||
{0.372768f, 0.073915f, 0.432400f},
|
||||
{0.379001f, 0.076253f, 0.432719f},
|
||||
{0.385228f, 0.078591f, 0.432955f},
|
||||
{0.391453f, 0.080927f, 0.433109f},
|
||||
{0.397674f, 0.083257f, 0.433183f},
|
||||
{0.403894f, 0.085580f, 0.433179f},
|
||||
{0.410113f, 0.087896f, 0.433098f},
|
||||
{0.416331f, 0.090203f, 0.432943f},
|
||||
{0.422549f, 0.092501f, 0.432714f},
|
||||
{0.428768f, 0.094790f, 0.432412f},
|
||||
{0.434987f, 0.097069f, 0.432039f},
|
||||
{0.441207f, 0.099338f, 0.431594f},
|
||||
{0.447428f, 0.101597f, 0.431080f},
|
||||
{0.453651f, 0.103848f, 0.430498f},
|
||||
{0.459875f, 0.106089f, 0.429846f},
|
||||
{0.466100f, 0.108322f, 0.429125f},
|
||||
{0.472328f, 0.110547f, 0.428334f},
|
||||
{0.478558f, 0.112764f, 0.427475f},
|
||||
{0.484789f, 0.114974f, 0.426548f},
|
||||
{0.491022f, 0.117179f, 0.425552f},
|
||||
{0.497257f, 0.119379f, 0.424488f},
|
||||
{0.503493f, 0.121575f, 0.423356f},
|
||||
{0.509730f, 0.123769f, 0.422156f},
|
||||
{0.515967f, 0.125960f, 0.420887f},
|
||||
{0.522206f, 0.128150f, 0.419549f},
|
||||
{0.528444f, 0.130341f, 0.418142f},
|
||||
{0.534683f, 0.132534f, 0.416667f},
|
||||
{0.540920f, 0.134729f, 0.415123f},
|
||||
{0.547157f, 0.136929f, 0.413511f},
|
||||
{0.553392f, 0.139134f, 0.411829f},
|
||||
{0.559624f, 0.141346f, 0.410078f},
|
||||
{0.565854f, 0.143567f, 0.408258f},
|
||||
{0.572081f, 0.145797f, 0.406369f},
|
||||
{0.578304f, 0.148039f, 0.404411f},
|
||||
{0.584521f, 0.150294f, 0.402385f},
|
||||
{0.590734f, 0.152563f, 0.400290f},
|
||||
{0.596940f, 0.154848f, 0.398125f},
|
||||
{0.603139f, 0.157151f, 0.395891f},
|
||||
{0.609330f, 0.159474f, 0.393589f},
|
||||
{0.615513f, 0.161817f, 0.391219f},
|
||||
{0.621685f, 0.164184f, 0.388781f},
|
||||
{0.627847f, 0.166575f, 0.386276f},
|
||||
{0.633998f, 0.168992f, 0.383704f},
|
||||
{0.640135f, 0.171438f, 0.381065f},
|
||||
{0.646260f, 0.173914f, 0.378359f},
|
||||
{0.652369f, 0.176421f, 0.375586f},
|
||||
{0.658463f, 0.178962f, 0.372748f},
|
||||
{0.664540f, 0.181539f, 0.369846f},
|
||||
{0.670599f, 0.184153f, 0.366879f},
|
||||
{0.676638f, 0.186807f, 0.363849f},
|
||||
{0.682656f, 0.189501f, 0.360757f},
|
||||
{0.688653f, 0.192239f, 0.357603f},
|
||||
{0.694627f, 0.195021f, 0.354388f},
|
||||
{0.700576f, 0.197851f, 0.351113f},
|
||||
{0.706500f, 0.200728f, 0.347777f},
|
||||
{0.712396f, 0.203656f, 0.344383f},
|
||||
{0.718264f, 0.206636f, 0.340931f},
|
||||
{0.724103f, 0.209670f, 0.337424f},
|
||||
{0.729909f, 0.212759f, 0.333861f},
|
||||
{0.735683f, 0.215906f, 0.330245f},
|
||||
{0.741423f, 0.219112f, 0.326576f},
|
||||
{0.747127f, 0.222378f, 0.322856f},
|
||||
{0.752794f, 0.225706f, 0.319085f},
|
||||
{0.758422f, 0.229097f, 0.315266f},
|
||||
{0.764010f, 0.232554f, 0.311399f},
|
||||
{0.769556f, 0.236077f, 0.307485f},
|
||||
{0.775059f, 0.239667f, 0.303526f},
|
||||
{0.780517f, 0.243327f, 0.299523f},
|
||||
{0.785929f, 0.247056f, 0.295477f},
|
||||
{0.791293f, 0.250856f, 0.291390f},
|
||||
{0.796607f, 0.254728f, 0.287264f},
|
||||
{0.801871f, 0.258674f, 0.283099f},
|
||||
{0.807082f, 0.262692f, 0.278898f},
|
||||
{0.812239f, 0.266786f, 0.274661f},
|
||||
{0.817341f, 0.270954f, 0.270390f},
|
||||
{0.822386f, 0.275197f, 0.266085f},
|
||||
{0.827372f, 0.279517f, 0.261750f},
|
||||
{0.832299f, 0.283913f, 0.257383f},
|
||||
{0.837165f, 0.288385f, 0.252988f},
|
||||
{0.841969f, 0.292933f, 0.248564f},
|
||||
{0.846709f, 0.297559f, 0.244113f},
|
||||
{0.851384f, 0.302260f, 0.239636f},
|
||||
{0.855992f, 0.307038f, 0.235133f},
|
||||
{0.860533f, 0.311892f, 0.230606f},
|
||||
{0.865006f, 0.316822f, 0.226055f},
|
||||
{0.869409f, 0.321827f, 0.221482f},
|
||||
{0.873741f, 0.326906f, 0.216886f},
|
||||
{0.878001f, 0.332060f, 0.212268f},
|
||||
{0.882188f, 0.337287f, 0.207628f},
|
||||
{0.886302f, 0.342586f, 0.202968f},
|
||||
{0.890341f, 0.347957f, 0.198286f},
|
||||
{0.894305f, 0.353399f, 0.193584f},
|
||||
{0.898192f, 0.358911f, 0.188860f},
|
||||
{0.902003f, 0.364492f, 0.184116f},
|
||||
{0.905735f, 0.370140f, 0.179350f},
|
||||
{0.909390f, 0.375856f, 0.174563f},
|
||||
{0.912966f, 0.381636f, 0.169755f},
|
||||
{0.916462f, 0.387481f, 0.164924f},
|
||||
{0.919879f, 0.393389f, 0.160070f},
|
||||
{0.923215f, 0.399359f, 0.155193f},
|
||||
{0.926470f, 0.405389f, 0.150292f},
|
||||
{0.929644f, 0.411479f, 0.145367f},
|
||||
{0.932737f, 0.417627f, 0.140417f},
|
||||
{0.935747f, 0.423831f, 0.135440f},
|
||||
{0.938675f, 0.430091f, 0.130438f},
|
||||
{0.941521f, 0.436405f, 0.125409f},
|
||||
{0.944285f, 0.442772f, 0.120354f},
|
||||
{0.946965f, 0.449191f, 0.115272f},
|
||||
{0.949562f, 0.455660f, 0.110164f},
|
||||
{0.952075f, 0.462178f, 0.105031f},
|
||||
{0.954506f, 0.468744f, 0.099874f},
|
||||
{0.956852f, 0.475356f, 0.094695f},
|
||||
{0.959114f, 0.482014f, 0.089499f},
|
||||
{0.961293f, 0.488716f, 0.084289f},
|
||||
{0.963387f, 0.495462f, 0.079073f},
|
||||
{0.965397f, 0.502249f, 0.073859f},
|
||||
{0.967322f, 0.509078f, 0.068659f},
|
||||
{0.969163f, 0.515946f, 0.063488f},
|
||||
{0.970919f, 0.522853f, 0.058367f},
|
||||
{0.972590f, 0.529798f, 0.053324f},
|
||||
{0.974176f, 0.536780f, 0.048392f},
|
||||
{0.975677f, 0.543798f, 0.043618f},
|
||||
{0.977092f, 0.550850f, 0.039050f},
|
||||
{0.978422f, 0.557937f, 0.034931f},
|
||||
{0.979666f, 0.565057f, 0.031409f},
|
||||
{0.980824f, 0.572209f, 0.028508f},
|
||||
{0.981895f, 0.579392f, 0.026250f},
|
||||
{0.982881f, 0.586606f, 0.024661f},
|
||||
{0.983779f, 0.593849f, 0.023770f},
|
||||
{0.984591f, 0.601122f, 0.023606f},
|
||||
{0.985315f, 0.608422f, 0.024202f},
|
||||
{0.985952f, 0.615750f, 0.025592f},
|
||||
{0.986502f, 0.623105f, 0.027814f},
|
||||
{0.986964f, 0.630485f, 0.030908f},
|
||||
{0.987337f, 0.637890f, 0.034916f},
|
||||
{0.987622f, 0.645320f, 0.039886f},
|
||||
{0.987819f, 0.652773f, 0.045581f},
|
||||
{0.987926f, 0.660250f, 0.051750f},
|
||||
{0.987945f, 0.667748f, 0.058329f},
|
||||
{0.987874f, 0.675267f, 0.065257f},
|
||||
{0.987714f, 0.682807f, 0.072489f},
|
||||
{0.987464f, 0.690366f, 0.079990f},
|
||||
{0.987124f, 0.697944f, 0.087731f},
|
||||
{0.986694f, 0.705540f, 0.095694f},
|
||||
{0.986175f, 0.713153f, 0.103863f},
|
||||
{0.985566f, 0.720782f, 0.112229f},
|
||||
{0.984865f, 0.728427f, 0.120785f},
|
||||
{0.984075f, 0.736087f, 0.129527f},
|
||||
{0.983196f, 0.743758f, 0.138453f},
|
||||
{0.982228f, 0.751442f, 0.147565f},
|
||||
{0.981173f, 0.759135f, 0.156863f},
|
||||
{0.980032f, 0.766837f, 0.166353f},
|
||||
{0.978806f, 0.774545f, 0.176037f},
|
||||
{0.977497f, 0.782258f, 0.185923f},
|
||||
{0.976108f, 0.789974f, 0.196018f},
|
||||
{0.974638f, 0.797692f, 0.206332f},
|
||||
{0.973088f, 0.805409f, 0.216877f},
|
||||
{0.971468f, 0.813122f, 0.227658f},
|
||||
{0.969783f, 0.820825f, 0.238686f},
|
||||
{0.968041f, 0.828515f, 0.249972f},
|
||||
{0.966243f, 0.836191f, 0.261534f},
|
||||
{0.964394f, 0.843848f, 0.273391f},
|
||||
{0.962517f, 0.851476f, 0.285546f},
|
||||
{0.960626f, 0.859069f, 0.298010f},
|
||||
{0.958720f, 0.866624f, 0.310820f},
|
||||
{0.956834f, 0.874129f, 0.323974f},
|
||||
{0.954997f, 0.881569f, 0.337475f},
|
||||
{0.953215f, 0.888942f, 0.351369f},
|
||||
{0.951546f, 0.896226f, 0.365627f},
|
||||
{0.950018f, 0.903409f, 0.380271f},
|
||||
{0.948683f, 0.910473f, 0.395289f},
|
||||
{0.947594f, 0.917399f, 0.410665f},
|
||||
{0.946809f, 0.924168f, 0.426373f},
|
||||
{0.946392f, 0.930761f, 0.442367f},
|
||||
{0.946403f, 0.937159f, 0.458592f},
|
||||
{0.946903f, 0.943348f, 0.474970f},
|
||||
{0.947937f, 0.949318f, 0.491426f},
|
||||
{0.949545f, 0.955063f, 0.507860f},
|
||||
{0.951740f, 0.960587f, 0.524203f},
|
||||
{0.954529f, 0.965896f, 0.540361f},
|
||||
{0.957896f, 0.971003f, 0.556275f},
|
||||
{0.961812f, 0.975924f, 0.571925f},
|
||||
{0.966249f, 0.980678f, 0.587206f},
|
||||
{0.971162f, 0.985282f, 0.602154f},
|
||||
{0.976511f, 0.989753f, 0.616760f},
|
||||
{0.982257f, 0.994109f, 0.631017f},
|
||||
{0.988362f, 0.998364f, 0.644924f}};
|
||||
|
||||
//Plasma data from MatPlotLib
|
||||
static std::vector<Color<float>> plasma_data = {
|
||||
{5.03832136e-02f, 2.98028976e-02f, 5.27974883e-01f},
|
||||
{6.35363639e-02f, 2.84259729e-02f, 5.33123681e-01f},
|
||||
{7.53531234e-02f, 2.72063728e-02f, 5.38007001e-01f},
|
||||
{8.62217979e-02f, 2.61253206e-02f, 5.42657691e-01f},
|
||||
{9.63786097e-02f, 2.51650976e-02f, 5.47103487e-01f},
|
||||
{1.05979704e-01f, 2.43092436e-02f, 5.51367851e-01f},
|
||||
{1.15123641e-01f, 2.35562500e-02f, 5.55467728e-01f},
|
||||
{1.23902903e-01f, 2.28781011e-02f, 5.59423480e-01f},
|
||||
{1.32380720e-01f, 2.22583774e-02f, 5.63250116e-01f},
|
||||
{1.40603076e-01f, 2.16866674e-02f, 5.66959485e-01f},
|
||||
{1.48606527e-01f, 2.11535876e-02f, 5.70561711e-01f},
|
||||
{1.56420649e-01f, 2.06507174e-02f, 5.74065446e-01f},
|
||||
{1.64069722e-01f, 2.01705326e-02f, 5.77478074e-01f},
|
||||
{1.71573925e-01f, 1.97063415e-02f, 5.80805890e-01f},
|
||||
{1.78950212e-01f, 1.92522243e-02f, 5.84054243e-01f},
|
||||
{1.86212958e-01f, 1.88029767e-02f, 5.87227661e-01f},
|
||||
{1.93374449e-01f, 1.83540593e-02f, 5.90329954e-01f},
|
||||
{2.00445260e-01f, 1.79015512e-02f, 5.93364304e-01f},
|
||||
{2.07434551e-01f, 1.74421086e-02f, 5.96333341e-01f},
|
||||
{2.14350298e-01f, 1.69729276e-02f, 5.99239207e-01f},
|
||||
{2.21196750e-01f, 1.64970484e-02f, 6.02083323e-01f},
|
||||
{2.27982971e-01f, 1.60071509e-02f, 6.04867403e-01f},
|
||||
{2.34714537e-01f, 1.55015065e-02f, 6.07592438e-01f},
|
||||
{2.41396253e-01f, 1.49791041e-02f, 6.10259089e-01f},
|
||||
{2.48032377e-01f, 1.44393586e-02f, 6.12867743e-01f},
|
||||
{2.54626690e-01f, 1.38820918e-02f, 6.15418537e-01f},
|
||||
{2.61182562e-01f, 1.33075156e-02f, 6.17911385e-01f},
|
||||
{2.67702993e-01f, 1.27162163e-02f, 6.20345997e-01f},
|
||||
{2.74190665e-01f, 1.21091423e-02f, 6.22721903e-01f},
|
||||
{2.80647969e-01f, 1.14875915e-02f, 6.25038468e-01f},
|
||||
{2.87076059e-01f, 1.08554862e-02f, 6.27294975e-01f},
|
||||
{2.93477695e-01f, 1.02128849e-02f, 6.29490490e-01f},
|
||||
{2.99855122e-01f, 9.56079551e-03f, 6.31623923e-01f},
|
||||
{3.06209825e-01f, 8.90185346e-03f, 6.33694102e-01f},
|
||||
{3.12543124e-01f, 8.23900704e-03f, 6.35699759e-01f},
|
||||
{3.18856183e-01f, 7.57551051e-03f, 6.37639537e-01f},
|
||||
{3.25150025e-01f, 6.91491734e-03f, 6.39512001e-01f},
|
||||
{3.31425547e-01f, 6.26107379e-03f, 6.41315649e-01f},
|
||||
{3.37683446e-01f, 5.61830889e-03f, 6.43048936e-01f},
|
||||
{3.43924591e-01f, 4.99053080e-03f, 6.44710195e-01f},
|
||||
{3.50149699e-01f, 4.38202557e-03f, 6.46297711e-01f},
|
||||
{3.56359209e-01f, 3.79781761e-03f, 6.47809772e-01f},
|
||||
{3.62553473e-01f, 3.24319591e-03f, 6.49244641e-01f},
|
||||
{3.68732762e-01f, 2.72370721e-03f, 6.50600561e-01f},
|
||||
{3.74897270e-01f, 2.24514897e-03f, 6.51875762e-01f},
|
||||
{3.81047116e-01f, 1.81356205e-03f, 6.53068467e-01f},
|
||||
{3.87182639e-01f, 1.43446923e-03f, 6.54176761e-01f},
|
||||
{3.93304010e-01f, 1.11388259e-03f, 6.55198755e-01f},
|
||||
{3.99410821e-01f, 8.59420809e-04f, 6.56132835e-01f},
|
||||
{4.05502914e-01f, 6.78091517e-04f, 6.56977276e-01f},
|
||||
{4.11580082e-01f, 5.77101735e-04f, 6.57730380e-01f},
|
||||
{4.17642063e-01f, 5.63847476e-04f, 6.58390492e-01f},
|
||||
{4.23688549e-01f, 6.45902780e-04f, 6.58956004e-01f},
|
||||
{4.29719186e-01f, 8.31008207e-04f, 6.59425363e-01f},
|
||||
{4.35733575e-01f, 1.12705875e-03f, 6.59797077e-01f},
|
||||
{4.41732123e-01f, 1.53984779e-03f, 6.60069009e-01f},
|
||||
{4.47713600e-01f, 2.07954744e-03f, 6.60240367e-01f},
|
||||
{4.53677394e-01f, 2.75470302e-03f, 6.60309966e-01f},
|
||||
{4.59622938e-01f, 3.57374415e-03f, 6.60276655e-01f},
|
||||
{4.65549631e-01f, 4.54518084e-03f, 6.60139383e-01f},
|
||||
{4.71456847e-01f, 5.67758762e-03f, 6.59897210e-01f},
|
||||
{4.77343929e-01f, 6.97958743e-03f, 6.59549311e-01f},
|
||||
{4.83210198e-01f, 8.45983494e-03f, 6.59094989e-01f},
|
||||
{4.89054951e-01f, 1.01269996e-02f, 6.58533677e-01f},
|
||||
{4.94877466e-01f, 1.19897486e-02f, 6.57864946e-01f},
|
||||
{5.00677687e-01f, 1.40550640e-02f, 6.57087561e-01f},
|
||||
{5.06454143e-01f, 1.63333443e-02f, 6.56202294e-01f},
|
||||
{5.12206035e-01f, 1.88332232e-02f, 6.55209222e-01f},
|
||||
{5.17932580e-01f, 2.15631918e-02f, 6.54108545e-01f},
|
||||
{5.23632990e-01f, 2.45316468e-02f, 6.52900629e-01f},
|
||||
{5.29306474e-01f, 2.77468735e-02f, 6.51586010e-01f},
|
||||
{5.34952244e-01f, 3.12170300e-02f, 6.50165396e-01f},
|
||||
{5.40569510e-01f, 3.49501310e-02f, 6.48639668e-01f},
|
||||
{5.46157494e-01f, 3.89540334e-02f, 6.47009884e-01f},
|
||||
{5.51715423e-01f, 4.31364795e-02f, 6.45277275e-01f},
|
||||
{5.57242538e-01f, 4.73307585e-02f, 6.43443250e-01f},
|
||||
{5.62738096e-01f, 5.15448092e-02f, 6.41509389e-01f},
|
||||
{5.68201372e-01f, 5.57776706e-02f, 6.39477440e-01f},
|
||||
{5.73631859e-01f, 6.00281369e-02f, 6.37348841e-01f},
|
||||
{5.79028682e-01f, 6.42955547e-02f, 6.35126108e-01f},
|
||||
{5.84391137e-01f, 6.85790261e-02f, 6.32811608e-01f},
|
||||
{5.89718606e-01f, 7.28775875e-02f, 6.30407727e-01f},
|
||||
{5.95010505e-01f, 7.71902878e-02f, 6.27916992e-01f},
|
||||
{6.00266283e-01f, 8.15161895e-02f, 6.25342058e-01f},
|
||||
{6.05485428e-01f, 8.58543713e-02f, 6.22685703e-01f},
|
||||
{6.10667469e-01f, 9.02039303e-02f, 6.19950811e-01f},
|
||||
{6.15811974e-01f, 9.45639838e-02f, 6.17140367e-01f},
|
||||
{6.20918555e-01f, 9.89336721e-02f, 6.14257440e-01f},
|
||||
{6.25986869e-01f, 1.03312160e-01f, 6.11305174e-01f},
|
||||
{6.31016615e-01f, 1.07698641e-01f, 6.08286774e-01f},
|
||||
{6.36007543e-01f, 1.12092335e-01f, 6.05205491e-01f},
|
||||
{6.40959444e-01f, 1.16492495e-01f, 6.02064611e-01f},
|
||||
{6.45872158e-01f, 1.20898405e-01f, 5.98867442e-01f},
|
||||
{6.50745571e-01f, 1.25309384e-01f, 5.95617300e-01f},
|
||||
{6.55579615e-01f, 1.29724785e-01f, 5.92317494e-01f},
|
||||
{6.60374266e-01f, 1.34143997e-01f, 5.88971318e-01f},
|
||||
{6.65129493e-01f, 1.38566428e-01f, 5.85582301e-01f},
|
||||
{6.69845385e-01f, 1.42991540e-01f, 5.82153572e-01f},
|
||||
{6.74522060e-01f, 1.47418835e-01f, 5.78688247e-01f},
|
||||
{6.79159664e-01f, 1.51847851e-01f, 5.75189431e-01f},
|
||||
{6.83758384e-01f, 1.56278163e-01f, 5.71660158e-01f},
|
||||
{6.88318440e-01f, 1.60709387e-01f, 5.68103380e-01f},
|
||||
{6.92840088e-01f, 1.65141174e-01f, 5.64521958e-01f},
|
||||
{6.97323615e-01f, 1.69573215e-01f, 5.60918659e-01f},
|
||||
{7.01769334e-01f, 1.74005236e-01f, 5.57296144e-01f},
|
||||
{7.06177590e-01f, 1.78437000e-01f, 5.53656970e-01f},
|
||||
{7.10548747e-01f, 1.82868306e-01f, 5.50003579e-01f},
|
||||
{7.14883195e-01f, 1.87298986e-01f, 5.46338299e-01f},
|
||||
{7.19181339e-01f, 1.91728906e-01f, 5.42663338e-01f},
|
||||
{7.23443604e-01f, 1.96157962e-01f, 5.38980786e-01f},
|
||||
{7.27670428e-01f, 2.00586086e-01f, 5.35292612e-01f},
|
||||
{7.31862231e-01f, 2.05013174e-01f, 5.31600995e-01f},
|
||||
{7.36019424e-01f, 2.09439071e-01f, 5.27908434e-01f},
|
||||
{7.40142557e-01f, 2.13863965e-01f, 5.24215533e-01f},
|
||||
{7.44232102e-01f, 2.18287899e-01f, 5.20523766e-01f},
|
||||
{7.48288533e-01f, 2.22710942e-01f, 5.16834495e-01f},
|
||||
{7.52312321e-01f, 2.27133187e-01f, 5.13148963e-01f},
|
||||
{7.56303937e-01f, 2.31554749e-01f, 5.09468305e-01f},
|
||||
{7.60263849e-01f, 2.35975765e-01f, 5.05793543e-01f},
|
||||
{7.64192516e-01f, 2.40396394e-01f, 5.02125599e-01f},
|
||||
{7.68090391e-01f, 2.44816813e-01f, 4.98465290e-01f},
|
||||
{7.71957916e-01f, 2.49237220e-01f, 4.94813338e-01f},
|
||||
{7.75795522e-01f, 2.53657797e-01f, 4.91170517e-01f},
|
||||
{7.79603614e-01f, 2.58078397e-01f, 4.87539124e-01f},
|
||||
{7.83382636e-01f, 2.62499662e-01f, 4.83917732e-01f},
|
||||
{7.87132978e-01f, 2.66921859e-01f, 4.80306702e-01f},
|
||||
{7.90855015e-01f, 2.71345267e-01f, 4.76706319e-01f},
|
||||
{7.94549101e-01f, 2.75770179e-01f, 4.73116798e-01f},
|
||||
{7.98215577e-01f, 2.80196901e-01f, 4.69538286e-01f},
|
||||
{8.01854758e-01f, 2.84625750e-01f, 4.65970871e-01f},
|
||||
{8.05466945e-01f, 2.89057057e-01f, 4.62414580e-01f},
|
||||
{8.09052419e-01f, 2.93491117e-01f, 4.58869577e-01f},
|
||||
{8.12611506e-01f, 2.97927865e-01f, 4.55337565e-01f},
|
||||
{8.16144382e-01f, 3.02368130e-01f, 4.51816385e-01f},
|
||||
{8.19651255e-01f, 3.06812282e-01f, 4.48305861e-01f},
|
||||
{8.23132309e-01f, 3.11260703e-01f, 4.44805781e-01f},
|
||||
{8.26587706e-01f, 3.15713782e-01f, 4.41315901e-01f},
|
||||
{8.30017584e-01f, 3.20171913e-01f, 4.37835947e-01f},
|
||||
{8.33422053e-01f, 3.24635499e-01f, 4.34365616e-01f},
|
||||
{8.36801237e-01f, 3.29104836e-01f, 4.30905052e-01f},
|
||||
{8.40155276e-01f, 3.33580106e-01f, 4.27454836e-01f},
|
||||
{8.43484103e-01f, 3.38062109e-01f, 4.24013059e-01f},
|
||||
{8.46787726e-01f, 3.42551272e-01f, 4.20579333e-01f},
|
||||
{8.50066132e-01f, 3.47048028e-01f, 4.17153264e-01f},
|
||||
{8.53319279e-01f, 3.51552815e-01f, 4.13734445e-01f},
|
||||
{8.56547103e-01f, 3.56066072e-01f, 4.10322469e-01f},
|
||||
{8.59749520e-01f, 3.60588229e-01f, 4.06916975e-01f},
|
||||
{8.62926559e-01f, 3.65119408e-01f, 4.03518809e-01f},
|
||||
{8.66077920e-01f, 3.69660446e-01f, 4.00126027e-01f},
|
||||
{8.69203436e-01f, 3.74211795e-01f, 3.96738211e-01f},
|
||||
{8.72302917e-01f, 3.78773910e-01f, 3.93354947e-01f},
|
||||
{8.75376149e-01f, 3.83347243e-01f, 3.89975832e-01f},
|
||||
{8.78422895e-01f, 3.87932249e-01f, 3.86600468e-01f},
|
||||
{8.81442916e-01f, 3.92529339e-01f, 3.83228622e-01f},
|
||||
{8.84435982e-01f, 3.97138877e-01f, 3.79860246e-01f},
|
||||
{8.87401682e-01f, 4.01761511e-01f, 3.76494232e-01f},
|
||||
{8.90339687e-01f, 4.06397694e-01f, 3.73130228e-01f},
|
||||
{8.93249647e-01f, 4.11047871e-01f, 3.69767893e-01f},
|
||||
{8.96131191e-01f, 4.15712489e-01f, 3.66406907e-01f},
|
||||
{8.98983931e-01f, 4.20391986e-01f, 3.63046965e-01f},
|
||||
{9.01807455e-01f, 4.25086807e-01f, 3.59687758e-01f},
|
||||
{9.04601295e-01f, 4.29797442e-01f, 3.56328796e-01f},
|
||||
{9.07364995e-01f, 4.34524335e-01f, 3.52969777e-01f},
|
||||
{9.10098088e-01f, 4.39267908e-01f, 3.49610469e-01f},
|
||||
{9.12800095e-01f, 4.44028574e-01f, 3.46250656e-01f},
|
||||
{9.15470518e-01f, 4.48806744e-01f, 3.42890148e-01f},
|
||||
{9.18108848e-01f, 4.53602818e-01f, 3.39528771e-01f},
|
||||
{9.20714383e-01f, 4.58417420e-01f, 3.36165582e-01f},
|
||||
{9.23286660e-01f, 4.63250828e-01f, 3.32800827e-01f},
|
||||
{9.25825146e-01f, 4.68103387e-01f, 3.29434512e-01f},
|
||||
{9.28329275e-01f, 4.72975465e-01f, 3.26066550e-01f},
|
||||
{9.30798469e-01f, 4.77867420e-01f, 3.22696876e-01f},
|
||||
{9.33232140e-01f, 4.82779603e-01f, 3.19325444e-01f},
|
||||
{9.35629684e-01f, 4.87712357e-01f, 3.15952211e-01f},
|
||||
{9.37990034e-01f, 4.92666544e-01f, 3.12575440e-01f},
|
||||
{9.40312939e-01f, 4.97642038e-01f, 3.09196628e-01f},
|
||||
{9.42597771e-01f, 5.02639147e-01f, 3.05815824e-01f},
|
||||
{9.44843893e-01f, 5.07658169e-01f, 3.02433101e-01f},
|
||||
{9.47050662e-01f, 5.12699390e-01f, 2.99048555e-01f},
|
||||
{9.49217427e-01f, 5.17763087e-01f, 2.95662308e-01f},
|
||||
{9.51343530e-01f, 5.22849522e-01f, 2.92274506e-01f},
|
||||
{9.53427725e-01f, 5.27959550e-01f, 2.88883445e-01f},
|
||||
{9.55469640e-01f, 5.33093083e-01f, 2.85490391e-01f},
|
||||
{9.57468770e-01f, 5.38250172e-01f, 2.82096149e-01f},
|
||||
{9.59424430e-01f, 5.43431038e-01f, 2.78700990e-01f},
|
||||
{9.61335930e-01f, 5.48635890e-01f, 2.75305214e-01f},
|
||||
{9.63202573e-01f, 5.53864931e-01f, 2.71909159e-01f},
|
||||
{9.65023656e-01f, 5.59118349e-01f, 2.68513200e-01f},
|
||||
{9.66798470e-01f, 5.64396327e-01f, 2.65117752e-01f},
|
||||
{9.68525639e-01f, 5.69699633e-01f, 2.61721488e-01f},
|
||||
{9.70204593e-01f, 5.75028270e-01f, 2.58325424e-01f},
|
||||
{9.71835007e-01f, 5.80382015e-01f, 2.54931256e-01f},
|
||||
{9.73416145e-01f, 5.85761012e-01f, 2.51539615e-01f},
|
||||
{9.74947262e-01f, 5.91165394e-01f, 2.48151200e-01f},
|
||||
{9.76427606e-01f, 5.96595287e-01f, 2.44766775e-01f},
|
||||
{9.77856416e-01f, 6.02050811e-01f, 2.41387186e-01f},
|
||||
{9.79232922e-01f, 6.07532077e-01f, 2.38013359e-01f},
|
||||
{9.80556344e-01f, 6.13039190e-01f, 2.34646316e-01f},
|
||||
{9.81825890e-01f, 6.18572250e-01f, 2.31287178e-01f},
|
||||
{9.83040742e-01f, 6.24131362e-01f, 2.27937141e-01f},
|
||||
{9.84198924e-01f, 6.29717516e-01f, 2.24595006e-01f},
|
||||
{9.85300760e-01f, 6.35329876e-01f, 2.21264889e-01f},
|
||||
{9.86345421e-01f, 6.40968508e-01f, 2.17948456e-01f},
|
||||
{9.87332067e-01f, 6.46633475e-01f, 2.14647532e-01f},
|
||||
{9.88259846e-01f, 6.52324832e-01f, 2.11364122e-01f},
|
||||
{9.89127893e-01f, 6.58042630e-01f, 2.08100426e-01f},
|
||||
{9.89935328e-01f, 6.63786914e-01f, 2.04858855e-01f},
|
||||
{9.90681261e-01f, 6.69557720e-01f, 2.01642049e-01f},
|
||||
{9.91364787e-01f, 6.75355082e-01f, 1.98452900e-01f},
|
||||
{9.91984990e-01f, 6.81179025e-01f, 1.95294567e-01f},
|
||||
{9.92540939e-01f, 6.87029567e-01f, 1.92170500e-01f},
|
||||
{9.93031693e-01f, 6.92906719e-01f, 1.89084459e-01f},
|
||||
{9.93456302e-01f, 6.98810484e-01f, 1.86040537e-01f},
|
||||
{9.93813802e-01f, 7.04740854e-01f, 1.83043180e-01f},
|
||||
{9.94103226e-01f, 7.10697814e-01f, 1.80097207e-01f},
|
||||
{9.94323596e-01f, 7.16681336e-01f, 1.77207826e-01f},
|
||||
{9.94473934e-01f, 7.22691379e-01f, 1.74380656e-01f},
|
||||
{9.94553260e-01f, 7.28727890e-01f, 1.71621733e-01f},
|
||||
{9.94560594e-01f, 7.34790799e-01f, 1.68937522e-01f},
|
||||
{9.94494964e-01f, 7.40880020e-01f, 1.66334918e-01f},
|
||||
{9.94355411e-01f, 7.46995448e-01f, 1.63821243e-01f},
|
||||
{9.94140989e-01f, 7.53136955e-01f, 1.61404226e-01f},
|
||||
{9.93850778e-01f, 7.59304390e-01f, 1.59091984e-01f},
|
||||
{9.93482190e-01f, 7.65498551e-01f, 1.56890625e-01f},
|
||||
{9.93033251e-01f, 7.71719833e-01f, 1.54807583e-01f},
|
||||
{9.92505214e-01f, 7.77966775e-01f, 1.52854862e-01f},
|
||||
{9.91897270e-01f, 7.84239120e-01f, 1.51041581e-01f},
|
||||
{9.91208680e-01f, 7.90536569e-01f, 1.49376885e-01f},
|
||||
{9.90438793e-01f, 7.96858775e-01f, 1.47869810e-01f},
|
||||
{9.89587065e-01f, 8.03205337e-01f, 1.46529128e-01f},
|
||||
{9.88647741e-01f, 8.09578605e-01f, 1.45357284e-01f},
|
||||
{9.87620557e-01f, 8.15977942e-01f, 1.44362644e-01f},
|
||||
{9.86509366e-01f, 8.22400620e-01f, 1.43556679e-01f},
|
||||
{9.85314198e-01f, 8.28845980e-01f, 1.42945116e-01f},
|
||||
{9.84031139e-01f, 8.35315360e-01f, 1.42528388e-01f},
|
||||
{9.82652820e-01f, 8.41811730e-01f, 1.42302653e-01f},
|
||||
{9.81190389e-01f, 8.48328902e-01f, 1.42278607e-01f},
|
||||
{9.79643637e-01f, 8.54866468e-01f, 1.42453425e-01f},
|
||||
{9.77994918e-01f, 8.61432314e-01f, 1.42808191e-01f},
|
||||
{9.76264977e-01f, 8.68015998e-01f, 1.43350944e-01f},
|
||||
{9.74443038e-01f, 8.74622194e-01f, 1.44061156e-01f},
|
||||
{9.72530009e-01f, 8.81250063e-01f, 1.44922913e-01f},
|
||||
{9.70532932e-01f, 8.87896125e-01f, 1.45918663e-01f},
|
||||
{9.68443477e-01f, 8.94563989e-01f, 1.47014438e-01f},
|
||||
{9.66271225e-01f, 9.01249365e-01f, 1.48179639e-01f},
|
||||
{9.64021057e-01f, 9.07950379e-01f, 1.49370428e-01f},
|
||||
{9.61681481e-01f, 9.14672479e-01f, 1.50520343e-01f},
|
||||
{9.59275646e-01f, 9.21406537e-01f, 1.51566019e-01f},
|
||||
{9.56808068e-01f, 9.28152065e-01f, 1.52409489e-01f},
|
||||
{9.54286813e-01f, 9.34907730e-01f, 1.52921158e-01f},
|
||||
{9.51726083e-01f, 9.41670605e-01f, 1.52925363e-01f},
|
||||
{9.49150533e-01f, 9.48434900e-01f, 1.52177604e-01f},
|
||||
{9.46602270e-01f, 9.55189860e-01f, 1.50327944e-01f},
|
||||
{9.44151742e-01f, 9.61916487e-01f, 1.46860789e-01f},
|
||||
{9.41896120e-01f, 9.68589814e-01f, 1.40955606e-01f},
|
||||
{9.40015097e-01f, 9.75158357e-01f, 1.31325517e-01f}};
|
||||
|
||||
//Viridis data from MatPlotLib
|
||||
static std::vector<Color<float>> viridis_data = {
|
||||
{0.26700401f, 0.00487433f, 0.32941519f},
|
||||
{0.26851048f, 0.00960483f, 0.33542652f},
|
||||
{0.26994384f, 0.01462494f, 0.34137895f},
|
||||
{0.27130489f, 0.01994186f, 0.34726862f},
|
||||
{0.27259384f, 0.02556309f, 0.35309303f},
|
||||
{0.27380934f, 0.03149748f, 0.35885256f},
|
||||
{0.27495242f, 0.03775181f, 0.36454323f},
|
||||
{0.27602238f, 0.04416723f, 0.37016418f},
|
||||
{0.2770184f , 0.05034437f, 0.37571452f},
|
||||
{0.27794143f, 0.05632444f, 0.38119074f},
|
||||
{0.27879067f, 0.06214536f, 0.38659204f},
|
||||
{0.2795655f , 0.06783587f, 0.39191723f},
|
||||
{0.28026658f, 0.07341724f, 0.39716349f},
|
||||
{0.28089358f, 0.07890703f, 0.40232944f},
|
||||
{0.28144581f, 0.0843197f , 0.40741404f},
|
||||
{0.28192358f, 0.08966622f, 0.41241521f},
|
||||
{0.28232739f, 0.09495545f, 0.41733086f},
|
||||
{0.28265633f, 0.10019576f, 0.42216032f},
|
||||
{0.28291049f, 0.10539345f, 0.42690202f},
|
||||
{0.28309095f, 0.11055307f, 0.43155375f},
|
||||
{0.28319704f, 0.11567966f, 0.43611482f},
|
||||
{0.28322882f, 0.12077701f, 0.44058404f},
|
||||
{0.28318684f, 0.12584799f, 0.44496f },
|
||||
{0.283072f , 0.13089477f, 0.44924127f},
|
||||
{0.28288389f, 0.13592005f, 0.45342734f},
|
||||
{0.28262297f, 0.14092556f, 0.45751726f},
|
||||
{0.28229037f, 0.14591233f, 0.46150995f},
|
||||
{0.28188676f, 0.15088147f, 0.46540474f},
|
||||
{0.28141228f, 0.15583425f, 0.46920128f},
|
||||
{0.28086773f, 0.16077132f, 0.47289909f},
|
||||
{0.28025468f, 0.16569272f, 0.47649762f},
|
||||
{0.27957399f, 0.17059884f, 0.47999675f},
|
||||
{0.27882618f, 0.1754902f , 0.48339654f},
|
||||
{0.27801236f, 0.18036684f, 0.48669702f},
|
||||
{0.27713437f, 0.18522836f, 0.48989831f},
|
||||
{0.27619376f, 0.19007447f, 0.49300074f},
|
||||
{0.27519116f, 0.1949054f , 0.49600488f},
|
||||
{0.27412802f, 0.19972086f, 0.49891131f},
|
||||
{0.27300596f, 0.20452049f, 0.50172076f},
|
||||
{0.27182812f, 0.20930306f, 0.50443413f},
|
||||
{0.27059473f, 0.21406899f, 0.50705243f},
|
||||
{0.26930756f, 0.21881782f, 0.50957678f},
|
||||
{0.26796846f, 0.22354911f, 0.5120084f },
|
||||
{0.26657984f, 0.2282621f , 0.5143487f },
|
||||
{0.2651445f , 0.23295593f, 0.5165993f },
|
||||
{0.2636632f , 0.23763078f, 0.51876163f},
|
||||
{0.26213801f, 0.24228619f, 0.52083736f},
|
||||
{0.26057103f, 0.2469217f , 0.52282822f},
|
||||
{0.25896451f, 0.25153685f, 0.52473609f},
|
||||
{0.25732244f, 0.2561304f , 0.52656332f},
|
||||
{0.25564519f, 0.26070284f, 0.52831152f},
|
||||
{0.25393498f, 0.26525384f, 0.52998273f},
|
||||
{0.25219404f, 0.26978306f, 0.53157905f},
|
||||
{0.25042462f, 0.27429024f, 0.53310261f},
|
||||
{0.24862899f, 0.27877509f, 0.53455561f},
|
||||
{0.2468114f , 0.28323662f, 0.53594093f},
|
||||
{0.24497208f, 0.28767547f, 0.53726018f},
|
||||
{0.24311324f, 0.29209154f, 0.53851561f},
|
||||
{0.24123708f, 0.29648471f, 0.53970946f},
|
||||
{0.23934575f, 0.30085494f, 0.54084398f},
|
||||
{0.23744138f, 0.30520222f, 0.5419214f },
|
||||
{0.23552606f, 0.30952657f, 0.54294396f},
|
||||
{0.23360277f, 0.31382773f, 0.54391424f},
|
||||
{0.2316735f , 0.3181058f , 0.54483444f},
|
||||
{0.22973926f, 0.32236127f, 0.54570633f},
|
||||
{0.22780192f, 0.32659432f, 0.546532f },
|
||||
{0.2258633f , 0.33080515f, 0.54731353f},
|
||||
{0.22392515f, 0.334994f , 0.54805291f},
|
||||
{0.22198915f, 0.33916114f, 0.54875211f},
|
||||
{0.22005691f, 0.34330688f, 0.54941304f},
|
||||
{0.21812995f, 0.34743154f, 0.55003755f},
|
||||
{0.21620971f, 0.35153548f, 0.55062743f},
|
||||
{0.21429757f, 0.35561907f, 0.5511844f },
|
||||
{0.21239477f, 0.35968273f, 0.55171011f},
|
||||
{0.2105031f , 0.36372671f, 0.55220646f},
|
||||
{0.20862342f, 0.36775151f, 0.55267486f},
|
||||
{0.20675628f, 0.37175775f, 0.55311653f},
|
||||
{0.20490257f, 0.37574589f, 0.55353282f},
|
||||
{0.20306309f, 0.37971644f, 0.55392505f},
|
||||
{0.20123854f, 0.38366989f, 0.55429441f},
|
||||
{0.1994295f , 0.38760678f, 0.55464205f},
|
||||
{0.1976365f , 0.39152762f, 0.55496905f},
|
||||
{0.19585993f, 0.39543297f, 0.55527637f},
|
||||
{0.19410009f, 0.39932336f, 0.55556494f},
|
||||
{0.19235719f, 0.40319934f, 0.55583559f},
|
||||
{0.19063135f, 0.40706148f, 0.55608907f},
|
||||
{0.18892259f, 0.41091033f, 0.55632606f},
|
||||
{0.18723083f, 0.41474645f, 0.55654717f},
|
||||
{0.18555593f, 0.4185704f , 0.55675292f},
|
||||
{0.18389763f, 0.42238275f, 0.55694377f},
|
||||
{0.18225561f, 0.42618405f, 0.5571201f },
|
||||
{0.18062949f, 0.42997486f, 0.55728221f},
|
||||
{0.17901879f, 0.43375572f, 0.55743035f},
|
||||
{0.17742298f, 0.4375272f , 0.55756466f},
|
||||
{0.17584148f, 0.44128981f, 0.55768526f},
|
||||
{0.17427363f, 0.4450441f , 0.55779216f},
|
||||
{0.17271876f, 0.4487906f , 0.55788532f},
|
||||
{0.17117615f, 0.4525298f , 0.55796464f},
|
||||
{0.16964573f, 0.45626209f, 0.55803034f},
|
||||
{0.16812641f, 0.45998802f, 0.55808199f},
|
||||
{0.1666171f , 0.46370813f, 0.55811913f},
|
||||
{0.16511703f, 0.4674229f , 0.55814141f},
|
||||
{0.16362543f, 0.47113278f, 0.55814842f},
|
||||
{0.16214155f, 0.47483821f, 0.55813967f},
|
||||
{0.16066467f, 0.47853961f, 0.55811466f},
|
||||
{0.15919413f, 0.4822374f , 0.5580728f },
|
||||
{0.15772933f, 0.48593197f, 0.55801347f},
|
||||
{0.15626973f, 0.4896237f , 0.557936f },
|
||||
{0.15481488f, 0.49331293f, 0.55783967f},
|
||||
{0.15336445f, 0.49700003f, 0.55772371f},
|
||||
{0.1519182f , 0.50068529f, 0.55758733f},
|
||||
{0.15047605f, 0.50436904f, 0.55742968f},
|
||||
{0.14903918f, 0.50805136f, 0.5572505f },
|
||||
{0.14760731f, 0.51173263f, 0.55704861f},
|
||||
{0.14618026f, 0.51541316f, 0.55682271f},
|
||||
{0.14475863f, 0.51909319f, 0.55657181f},
|
||||
{0.14334327f, 0.52277292f, 0.55629491f},
|
||||
{0.14193527f, 0.52645254f, 0.55599097f},
|
||||
{0.14053599f, 0.53013219f, 0.55565893f},
|
||||
{0.13914708f, 0.53381201f, 0.55529773f},
|
||||
{0.13777048f, 0.53749213f, 0.55490625f},
|
||||
{0.1364085f , 0.54117264f, 0.55448339f},
|
||||
{0.13506561f, 0.54485335f, 0.55402906f},
|
||||
{0.13374299f, 0.54853458f, 0.55354108f},
|
||||
{0.13244401f, 0.55221637f, 0.55301828f},
|
||||
{0.13117249f, 0.55589872f, 0.55245948f},
|
||||
{0.1299327f , 0.55958162f, 0.55186354f},
|
||||
{0.12872938f, 0.56326503f, 0.55122927f},
|
||||
{0.12756771f, 0.56694891f, 0.55055551f},
|
||||
{0.12645338f, 0.57063316f, 0.5498411f },
|
||||
{0.12539383f, 0.57431754f, 0.54908564f},
|
||||
{0.12439474f, 0.57800205f, 0.5482874f },
|
||||
{0.12346281f, 0.58168661f, 0.54744498f},
|
||||
{0.12260562f, 0.58537105f, 0.54655722f},
|
||||
{0.12183122f, 0.58905521f, 0.54562298f},
|
||||
{0.12114807f, 0.59273889f, 0.54464114f},
|
||||
{0.12056501f, 0.59642187f, 0.54361058f},
|
||||
{0.12009154f, 0.60010387f, 0.54253043f},
|
||||
{0.11973756f, 0.60378459f, 0.54139999f},
|
||||
{0.11951163f, 0.60746388f, 0.54021751f},
|
||||
{0.11942341f, 0.61114146f, 0.53898192f},
|
||||
{0.11948255f, 0.61481702f, 0.53769219f},
|
||||
{0.11969858f, 0.61849025f, 0.53634733f},
|
||||
{0.12008079f, 0.62216081f, 0.53494633f},
|
||||
{0.12063824f, 0.62582833f, 0.53348834f},
|
||||
{0.12137972f, 0.62949242f, 0.53197275f},
|
||||
{0.12231244f, 0.63315277f, 0.53039808f},
|
||||
{0.12344358f, 0.63680899f, 0.52876343f},
|
||||
{0.12477953f, 0.64046069f, 0.52706792f},
|
||||
{0.12632581f, 0.64410744f, 0.52531069f},
|
||||
{0.12808703f, 0.64774881f, 0.52349092f},
|
||||
{0.13006688f, 0.65138436f, 0.52160791f},
|
||||
{0.13226797f, 0.65501363f, 0.51966086f},
|
||||
{0.13469183f, 0.65863619f, 0.5176488f },
|
||||
{0.13733921f, 0.66225157f, 0.51557101f},
|
||||
{0.14020991f, 0.66585927f, 0.5134268f },
|
||||
{0.14330291f, 0.66945881f, 0.51121549f},
|
||||
{0.1466164f , 0.67304968f, 0.50893644f},
|
||||
{0.15014782f, 0.67663139f, 0.5065889f },
|
||||
{0.15389405f, 0.68020343f, 0.50417217f},
|
||||
{0.15785146f, 0.68376525f, 0.50168574f},
|
||||
{0.16201598f, 0.68731632f, 0.49912906f},
|
||||
{0.1663832f , 0.69085611f, 0.49650163f},
|
||||
{0.1709484f , 0.69438405f, 0.49380294f},
|
||||
{0.17570671f, 0.6978996f , 0.49103252f},
|
||||
{0.18065314f, 0.70140222f, 0.48818938f},
|
||||
{0.18578266f, 0.70489133f, 0.48527326f},
|
||||
{0.19109018f, 0.70836635f, 0.48228395f},
|
||||
{0.19657063f, 0.71182668f, 0.47922108f},
|
||||
{0.20221902f, 0.71527175f, 0.47608431f},
|
||||
{0.20803045f, 0.71870095f, 0.4728733f },
|
||||
{0.21400015f, 0.72211371f, 0.46958774f},
|
||||
{0.22012381f, 0.72550945f, 0.46622638f},
|
||||
{0.2263969f , 0.72888753f, 0.46278934f},
|
||||
{0.23281498f, 0.73224735f, 0.45927675f},
|
||||
{0.2393739f , 0.73558828f, 0.45568838f},
|
||||
{0.24606968f, 0.73890972f, 0.45202405f},
|
||||
{0.25289851f, 0.74221104f, 0.44828355f},
|
||||
{0.25985676f, 0.74549162f, 0.44446673f},
|
||||
{0.26694127f, 0.74875084f, 0.44057284f},
|
||||
{0.27414922f, 0.75198807f, 0.4366009f },
|
||||
{0.28147681f, 0.75520266f, 0.43255207f},
|
||||
{0.28892102f, 0.75839399f, 0.42842626f},
|
||||
{0.29647899f, 0.76156142f, 0.42422341f},
|
||||
{0.30414796f, 0.76470433f, 0.41994346f},
|
||||
{0.31192534f, 0.76782207f, 0.41558638f},
|
||||
{0.3198086f , 0.77091403f, 0.41115215f},
|
||||
{0.3277958f , 0.77397953f, 0.40664011f},
|
||||
{0.33588539f, 0.7770179f , 0.40204917f},
|
||||
{0.34407411f, 0.78002855f, 0.39738103f},
|
||||
{0.35235985f, 0.78301086f, 0.39263579f},
|
||||
{0.36074053f, 0.78596419f, 0.38781353f},
|
||||
{0.3692142f , 0.78888793f, 0.38291438f},
|
||||
{0.37777892f, 0.79178146f, 0.3779385f },
|
||||
{0.38643282f, 0.79464415f, 0.37288606f},
|
||||
{0.39517408f, 0.79747541f, 0.36775726f},
|
||||
{0.40400101f, 0.80027461f, 0.36255223f},
|
||||
{0.4129135f , 0.80304099f, 0.35726893f},
|
||||
{0.42190813f, 0.80577412f, 0.35191009f},
|
||||
{0.43098317f, 0.80847343f, 0.34647607f},
|
||||
{0.44013691f, 0.81113836f, 0.3409673f },
|
||||
{0.44936763f, 0.81376835f, 0.33538426f},
|
||||
{0.45867362f, 0.81636288f, 0.32972749f},
|
||||
{0.46805314f, 0.81892143f, 0.32399761f},
|
||||
{0.47750446f, 0.82144351f, 0.31819529f},
|
||||
{0.4870258f , 0.82392862f, 0.31232133f},
|
||||
{0.49661536f, 0.82637633f, 0.30637661f},
|
||||
{0.5062713f , 0.82878621f, 0.30036211f},
|
||||
{0.51599182f, 0.83115784f, 0.29427888f},
|
||||
{0.52577622f, 0.83349064f, 0.2881265f },
|
||||
{0.5356211f , 0.83578452f, 0.28190832f},
|
||||
{0.5455244f , 0.83803918f, 0.27562602f},
|
||||
{0.55548397f, 0.84025437f, 0.26928147f},
|
||||
{0.5654976f , 0.8424299f , 0.26287683f},
|
||||
{0.57556297f, 0.84456561f, 0.25641457f},
|
||||
{0.58567772f, 0.84666139f, 0.24989748f},
|
||||
{0.59583934f, 0.84871722f, 0.24332878f},
|
||||
{0.60604528f, 0.8507331f , 0.23671214f},
|
||||
{0.61629283f, 0.85270912f, 0.23005179f},
|
||||
{0.62657923f, 0.85464543f, 0.22335258f},
|
||||
{0.63690157f, 0.85654226f, 0.21662012f},
|
||||
{0.64725685f, 0.85839991f, 0.20986086f},
|
||||
{0.65764197f, 0.86021878f, 0.20308229f},
|
||||
{0.66805369f, 0.86199932f, 0.19629307f},
|
||||
{0.67848868f, 0.86374211f, 0.18950326f},
|
||||
{0.68894351f, 0.86544779f, 0.18272455f},
|
||||
{0.69941463f, 0.86711711f, 0.17597055f},
|
||||
{0.70989842f, 0.86875092f, 0.16925712f},
|
||||
{0.72039115f, 0.87035015f, 0.16260273f},
|
||||
{0.73088902f, 0.87191584f, 0.15602894f},
|
||||
{0.74138803f, 0.87344918f, 0.14956101f},
|
||||
{0.75188414f, 0.87495143f, 0.14322828f},
|
||||
{0.76237342f, 0.87642392f, 0.13706449f},
|
||||
{0.77285183f, 0.87786808f, 0.13110864f},
|
||||
{0.78331535f, 0.87928545f, 0.12540538f},
|
||||
{0.79375994f, 0.88067763f, 0.12000532f},
|
||||
{0.80418159f, 0.88204632f, 0.11496505f},
|
||||
{0.81457634f, 0.88339329f, 0.11034678f},
|
||||
{0.82494028f, 0.88472036f, 0.10621724f},
|
||||
{0.83526959f, 0.88602943f, 0.1026459f },
|
||||
{0.84556056f, 0.88732243f, 0.09970219f},
|
||||
{0.8558096f , 0.88860134f, 0.09745186f},
|
||||
{0.86601325f, 0.88986815f, 0.09595277f},
|
||||
{0.87616824f, 0.89112487f, 0.09525046f},
|
||||
{0.88627146f, 0.89237353f, 0.09537439f},
|
||||
{0.89632002f, 0.89361614f, 0.09633538f},
|
||||
{0.90631121f, 0.89485467f, 0.09812496f},
|
||||
{0.91624212f, 0.89609127f, 0.1007168f },
|
||||
{0.92610579f, 0.89732977f, 0.10407067f},
|
||||
{0.93590444f, 0.8985704f , 0.10813094f},
|
||||
{0.94563626f, 0.899815f , 0.11283773f},
|
||||
{0.95529972f, 0.90106534f, 0.11812832f},
|
||||
{0.96489353f, 0.90232311f, 0.12394051f},
|
||||
{0.97441665f, 0.90358991f, 0.13021494f},
|
||||
{0.98386829f, 0.90486726f, 0.13689671f},
|
||||
{0.99324789f, 0.90615657f, 0.1439362f }};
|
||||
|
||||
ColorMap::ColorMap(float min_val, float max_val, const std::vector<Color<float>>& color_data)
|
||||
: min_(min_val)
|
||||
, max_(max_val)
|
||||
, color_data_(color_data) {
|
||||
VTR_ASSERT(max_ >= min_);
|
||||
}
|
||||
|
||||
Color<float> ColorMap::color(float value) const {
|
||||
VTR_ASSERT(value >= min_);
|
||||
VTR_ASSERT(value <= max_);
|
||||
|
||||
float norm_value;
|
||||
if (range() == 0) {
|
||||
norm_value = 0;
|
||||
} else {
|
||||
norm_value = (value - min_) / range();
|
||||
}
|
||||
|
||||
size_t color_idx = std::round(norm_value * (color_data_.size() - 1));
|
||||
|
||||
VTR_ASSERT(color_idx < color_data_.size());
|
||||
|
||||
return color_data_[color_idx];
|
||||
}
|
||||
|
||||
float ColorMap::min() const {
|
||||
return min_;
|
||||
}
|
||||
|
||||
float ColorMap::max() const {
|
||||
return max_;
|
||||
}
|
||||
|
||||
float ColorMap::range() const {
|
||||
return max() - min();
|
||||
}
|
||||
|
||||
InfernoColorMap::InfernoColorMap(float min_val, float max_val)
|
||||
: ColorMap(min_val, max_val, inferno_data) {}
|
||||
|
||||
PlasmaColorMap::PlasmaColorMap(float min_val, float max_val)
|
||||
: ColorMap(min_val, max_val, plasma_data) {}
|
||||
|
||||
ViridisColorMap::ViridisColorMap(float min_val, float max_val)
|
||||
: ColorMap(min_val, max_val, viridis_data) {}
|
||||
|
||||
} //namespace
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef VTR_CMAP_H
|
||||
#define VTR_CMAP_H
|
||||
#include <vector>
|
||||
|
||||
namespace vtr {
|
||||
|
||||
template<class T>
|
||||
struct Color {
|
||||
T r;
|
||||
T g;
|
||||
T b;
|
||||
};
|
||||
|
||||
|
||||
class ColorMap {
|
||||
public:
|
||||
ColorMap(float min, float max, const std::vector<Color<float>>& color_data);
|
||||
virtual ~ColorMap() = default;
|
||||
Color<float> color(float value) const;
|
||||
float min() const;
|
||||
float max() const;
|
||||
float range() const;
|
||||
private:
|
||||
float min_;
|
||||
float max_;
|
||||
std::vector<Color<float>> color_data_;
|
||||
};
|
||||
|
||||
class InfernoColorMap : public ColorMap {
|
||||
public:
|
||||
InfernoColorMap(float min, float max);
|
||||
};
|
||||
|
||||
class PlasmaColorMap : public ColorMap {
|
||||
public:
|
||||
PlasmaColorMap(float min, float max);
|
||||
};
|
||||
|
||||
class ViridisColorMap : public ColorMap {
|
||||
public:
|
||||
ViridisColorMap(float min, float max);
|
||||
};
|
||||
|
||||
} //namespace
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
#include "vtr_digest.h"
|
||||
#include "vtr_error.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <array>
|
||||
|
||||
#include "picosha2.h"
|
||||
|
||||
namespace vtr {
|
||||
|
||||
std::string secure_digest_file(const std::string& filepath) {
|
||||
std::ifstream is(filepath);
|
||||
if (!is) {
|
||||
throw VtrError("Failed to open file", filepath);
|
||||
}
|
||||
return secure_digest_stream(is);
|
||||
}
|
||||
|
||||
std::string secure_digest_stream(std::istream& is) {
|
||||
//Read the stream in chunks and calculate the SHA256 digest
|
||||
picosha2::hash256_one_by_one hasher;
|
||||
|
||||
std::array<char, 1024> buf;
|
||||
while(!is.eof()) {
|
||||
//Process a chunk
|
||||
is.read(buf.data(), buf.size());
|
||||
hasher.process(buf.begin(), buf.begin() + is.gcount());
|
||||
}
|
||||
hasher.finish();
|
||||
|
||||
//Return the digest as a hex string, prefixed with the hash type
|
||||
//
|
||||
//Prefixing with the hash type should allow us to differentiate if the
|
||||
//hash type is ever changed in the future
|
||||
return "SHA256:" + picosha2::get_hash_hex_string(hasher);
|
||||
}
|
||||
|
||||
} //namespace
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef VTR_DIGEST_H
|
||||
#define VTR_DIGEST_H
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
|
||||
namespace vtr {
|
||||
|
||||
//Generate a secure hash of the file at filepath
|
||||
std::string secure_digest_file(const std::string& filepath);
|
||||
|
||||
//Generate a secure hash of a stream
|
||||
std::string secure_digest_stream(std::istream& is);
|
||||
|
||||
} //namespace
|
||||
|
||||
#endif
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef VTR_ERROR_H
|
||||
#define VTR_ERROR_H
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace vtr {
|
||||
|
||||
class VtrError : public std::runtime_error {
|
||||
public:
|
||||
VtrError(std::string msg="", std::string new_filename="", size_t new_linenumber=-1)
|
||||
: std::runtime_error(msg)
|
||||
, filename_(new_filename)
|
||||
, linenumber_(new_linenumber) {}
|
||||
|
||||
//Returns the filename associated with this error
|
||||
//returns an empty string if none is specified
|
||||
std::string filename() const { return filename_; }
|
||||
const char* filename_c_str() const { return filename_.c_str(); }
|
||||
|
||||
//Returns the line number associated with this error
|
||||
//returns zero if none is specified
|
||||
size_t line() const { return linenumber_; }
|
||||
|
||||
private:
|
||||
std::string filename_;
|
||||
size_t linenumber_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,349 @@
|
|||
#ifndef VTR_FLAT_MAP
|
||||
#define VTR_FLAT_MAP
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
|
||||
namespace vtr {
|
||||
|
||||
//Forward declaration
|
||||
template<class K, class V, class Compare=std::less<K>>
|
||||
class flat_map;
|
||||
|
||||
template<class K, class V, class Compare=std::less<K>>
|
||||
class flat_map2;
|
||||
|
||||
//Helper function to create a flat map from a vector of pairs
|
||||
//without haveing to explicity specify the key and value types
|
||||
template<class K, class V>
|
||||
flat_map<K,V> make_flat_map(std::vector<std::pair<K, V>> vec) {
|
||||
return flat_map<K,V>(vec);
|
||||
}
|
||||
|
||||
//
|
||||
// flat_map is a (nearly) std::map compatible container which uses a vector
|
||||
// as it's underlying storage. Internally the stored elements are kept sorted
|
||||
// allowing efficient look-up in O(logN) time via binary search.
|
||||
//
|
||||
// This container is typically useful in the following scenarios:
|
||||
// * Reduced memory usage if key/value are small (std::map needs to store pointers to
|
||||
// other BST nodes which can add substantial overhead for small keys/values)
|
||||
// * Faster search/iteration by exploiting data locality (all elments are in continguous
|
||||
// memory enabling better spatial locality)
|
||||
//
|
||||
// The container deviates from the behaviour of std::map in the following important ways:
|
||||
// * Insertion/erase takes O(N) instead of O(logN) time
|
||||
// * Iterators may be invalidated on insertion/erase (i.e. if the vector is reallocated)
|
||||
//
|
||||
// The slow insertion/erase performance makes this container poorly suited to maps that
|
||||
// frequently add/remove new keys. If this is required you likely want std::map or
|
||||
// std::unordered_map. However if the map is constructed once and then repeatedly quieried,
|
||||
// consider using the range or vector-based constructors which initializes the flat_map in
|
||||
// O(NlogN) time.
|
||||
//
|
||||
template<class K, class T, class Compare>
|
||||
class flat_map {
|
||||
public:
|
||||
typedef K key_type;
|
||||
typedef T mapped_type;
|
||||
typedef std::pair<K,T> value_type;
|
||||
typedef Compare key_compare;
|
||||
typedef value_type& reference;
|
||||
typedef const value_type& const_reference;
|
||||
typedef typename std::vector<value_type>::iterator iterator;
|
||||
typedef typename std::vector<value_type>::const_iterator const_iterator;
|
||||
typedef typename std::vector<value_type>::reverse_iterator reverse_iterator;
|
||||
typedef typename std::vector<value_type>::const_reverse_iterator const_reverse_iterator;
|
||||
typedef typename std::vector<value_type>::difference_type difference_type;
|
||||
typedef typename std::vector<value_type>::size_type size_type;
|
||||
|
||||
class value_compare;
|
||||
|
||||
public:
|
||||
//Standard big 5
|
||||
flat_map() = default;
|
||||
flat_map(const flat_map&) = default;
|
||||
flat_map(flat_map&&) = default;
|
||||
flat_map& operator=(const flat_map&) = default;
|
||||
flat_map& operator=(flat_map&&) = default;
|
||||
|
||||
//range constructor
|
||||
template <class InputIterator>
|
||||
flat_map(InputIterator first, InputIterator last) {
|
||||
//Copy the values
|
||||
std::copy(first, last, std::back_inserter(vec_));
|
||||
|
||||
sort();
|
||||
uniquify();
|
||||
}
|
||||
|
||||
//direct vector constructor
|
||||
flat_map(std::vector<value_type> values) {
|
||||
//By moving the values this should be more efficient
|
||||
//than the range constructor which must copy each element
|
||||
vec_ = std::move(values);
|
||||
|
||||
sort();
|
||||
uniquify();
|
||||
}
|
||||
|
||||
iterator begin() { return vec_.begin(); }
|
||||
const_iterator begin() const { return vec_.begin(); }
|
||||
iterator end() { return vec_.end(); }
|
||||
const_iterator end() const { return vec_.end(); }
|
||||
reverse_iterator rbegin() { return vec_.rbegin(); }
|
||||
const_reverse_iterator rbegin() const { return vec_.rbegin(); }
|
||||
reverse_iterator rend() { return vec_.rend(); }
|
||||
const_reverse_iterator rend() const { return vec_.rend(); }
|
||||
const_iterator cbegin() const { return vec_.begin(); }
|
||||
const_iterator cend() const { return vec_.end(); }
|
||||
const_reverse_iterator crbegin() const { return vec_.rbegin(); }
|
||||
const_reverse_iterator crend() const { return vec_.rend(); }
|
||||
|
||||
bool empty() const { return vec_.empty(); }
|
||||
size_type size() const { return vec_.size(); }
|
||||
size_type max_size() const { return vec_.max_size(); }
|
||||
|
||||
const mapped_type& operator[](const key_type& key) const {
|
||||
auto iter = find(key);
|
||||
if(iter == end()) {
|
||||
//Not found
|
||||
throw std::out_of_range("Invalid key");
|
||||
}
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
mapped_type& operator[](const key_type& key) {
|
||||
auto iter = find(key);
|
||||
if(iter == end()) {
|
||||
//Not found
|
||||
iter = insert(std::make_pair(key,mapped_type())).first;
|
||||
}
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
mapped_type& at(const key_type& key) {
|
||||
return const_cast<mapped_type&>(const_cast<const flat_map*>(this)->at(key));
|
||||
}
|
||||
|
||||
const mapped_type& at(const key_type& key) const {
|
||||
auto iter = find(key);
|
||||
if(iter == end()) {
|
||||
throw std::out_of_range("Invalid key");
|
||||
}
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
//Insert value
|
||||
std::pair<iterator,bool> insert(const value_type& value) {
|
||||
auto iter = lower_bound(value.first);
|
||||
if(iter != end() && keys_equivalent(iter->first, value.first)) {
|
||||
//Found existing
|
||||
return std::make_pair(iter, false);
|
||||
} else {
|
||||
//Insert
|
||||
iter = insert(iter, value);
|
||||
|
||||
return std::make_pair(iter, true);
|
||||
}
|
||||
}
|
||||
|
||||
//Insert value with position hint
|
||||
iterator insert(const_iterator position, const value_type& value) {
|
||||
//In a legal position
|
||||
VTR_ASSERT(position == begin() || value_comp()(*(position - 1), value));
|
||||
VTR_ASSERT(position == --end() || position == end() || !value_comp()(*(position + 1), value));
|
||||
|
||||
iterator iter = vec_.insert(position, value);
|
||||
|
||||
return iter;
|
||||
}
|
||||
|
||||
//Insert range
|
||||
template<class InputIterator>
|
||||
void insert(InputIterator first, InputIterator last) {
|
||||
vec_.insert(vec_.end(), first, last);
|
||||
|
||||
//TODO: could be more efficient
|
||||
sort();
|
||||
uniquify();
|
||||
}
|
||||
|
||||
//Erase by key
|
||||
void erase(const key_type& key) {
|
||||
auto iter = find(key);
|
||||
if(iter != end()) {
|
||||
vec_.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
//Erase at iterator
|
||||
void erase(const_iterator position) {
|
||||
vec_.erase(position);
|
||||
}
|
||||
|
||||
//Erase range
|
||||
void erase(const_iterator first, const_iterator last) {
|
||||
vec_.erase(first,last);
|
||||
}
|
||||
|
||||
void swap(flat_map& other) { std::swap(*this, other); }
|
||||
|
||||
void clear() { vec_.clear(); }
|
||||
|
||||
template<class ...Args>
|
||||
iterator emplace(const key_type& key, Args&&... args) {
|
||||
auto iter = lower_bound(key);
|
||||
if(iter != end() && keys_equivalent(iter->first, key)) {
|
||||
//Found
|
||||
return std::make_pair(iter, false);
|
||||
} else {
|
||||
//Emplace
|
||||
iter = emplace_hint(iter, key, std::forward<Args>(args)...);
|
||||
return std::make_pair(iter, true);
|
||||
}
|
||||
}
|
||||
|
||||
template<class ...Args>
|
||||
iterator emplace_hint(const_iterator position, Args&&... args) {
|
||||
return vec_.emplace(position, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void reserve(size_type n) { vec_.reserve(n); }
|
||||
void shrink_to_fit() { vec_.shrink_to_fit(); }
|
||||
|
||||
key_compare key_comp() const { return key_compare(); }
|
||||
value_compare value_comp() const { return value_compare(key_comp()); }
|
||||
|
||||
iterator find(const key_type& key) {
|
||||
const_iterator const_iter = const_cast<const flat_map*>(this)->find(key);
|
||||
return convert_to_iterator(const_iter);
|
||||
}
|
||||
|
||||
const_iterator find(const key_type& key) const {
|
||||
auto iter = lower_bound(key);
|
||||
if(iter != end() && keys_equivalent(iter->first, key)) {
|
||||
//Found
|
||||
return iter;
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
size_type count(const key_type& key) const {
|
||||
return (find(key) == end()) ? 0 : 1;
|
||||
}
|
||||
|
||||
iterator lower_bound(const key_type& key) {
|
||||
const_iterator const_iter = const_cast<const flat_map*>(this)->lower_bound(key);
|
||||
return convert_to_iterator(const_iter);
|
||||
}
|
||||
|
||||
const_iterator lower_bound(const key_type& key) const {
|
||||
return std::lower_bound(begin(), end(), key, value_comp());
|
||||
}
|
||||
|
||||
iterator upper_bound(const key_type& key) {
|
||||
const_iterator const_iter = const_cast<const flat_map*>(this)->upper_bound(key);
|
||||
return convert_to_iterator(const_iter);
|
||||
}
|
||||
|
||||
const_iterator upper_bound(const key_type& key) const {
|
||||
return std::upper_bound(begin(), end(), key, key_compare());
|
||||
}
|
||||
|
||||
std::pair<iterator,iterator> equal_range(const key_type& key) {
|
||||
auto const_iter_pair = const_cast<const flat_map*>(this)->equal_range(key);
|
||||
return std::pair<iterator,iterator>(iterator(const_iter_pair.first), iterator(const_iter_pair.second));
|
||||
}
|
||||
|
||||
std::pair<const_iterator,const_iterator> equal_range(const key_type& key) const {
|
||||
return std::equal_range(begin(), end(), key);
|
||||
}
|
||||
|
||||
public:
|
||||
friend void swap(flat_map& lhs, flat_map& rhs) { std::swap(lhs.vec_, rhs.vec_); }
|
||||
|
||||
private:
|
||||
bool keys_equivalent(const key_type& lhs, const key_type& rhs) const {
|
||||
return !key_comp()(lhs, rhs) && !key_comp()(rhs, lhs);
|
||||
}
|
||||
|
||||
void sort() {
|
||||
std::sort(vec_.begin(), vec_.end(), value_comp());
|
||||
}
|
||||
|
||||
void uniquify() {
|
||||
//Uniquify
|
||||
auto key_equal_pred = [this](const value_type& lhs, const value_type& rhs) {
|
||||
return !value_comp()(lhs, rhs) && !value_comp()(rhs, lhs);
|
||||
};
|
||||
vec_.erase(std::unique(vec_.begin(), vec_.end(), key_equal_pred), vec_.end());
|
||||
}
|
||||
|
||||
iterator convert_to_iterator(const_iterator const_iter) {
|
||||
//This is a work around for the fact that there is no conversion between
|
||||
//a const_iterator and iterator.
|
||||
//
|
||||
//We intiailize i to the start of the container and then advance it by
|
||||
//the distance to const_iter. The resulting i points to the same element
|
||||
//as const_iter
|
||||
//
|
||||
//Note that to be able to call std::distance with an iterator and
|
||||
//const_iterator we need to specify the type as const_iterator (relying
|
||||
//on the implicit conversion from iterator to const_iterator for i)
|
||||
//
|
||||
//Since the iterators are really vector (i.e. random-access) iterators
|
||||
//this takes constant time
|
||||
iterator i = begin();
|
||||
std::advance(i, std::distance<const_iterator>(i, const_iter));
|
||||
return i;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<value_type> vec_;
|
||||
};
|
||||
|
||||
//Like flat_map, but operator[] never inserts and directly returns the mapped value
|
||||
template<class K, class T, class Compare>
|
||||
class flat_map2 : public flat_map<K,T,Compare> {
|
||||
public:
|
||||
|
||||
const T& operator[](const K& key) const {
|
||||
auto itr = this->find(key);
|
||||
if (itr == this->end()) {
|
||||
throw std::logic_error("Key not found");
|
||||
}
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
T& operator[](const K& key) {
|
||||
return const_cast<T&>(const_cast<const flat_map2*>(this)->operator[](key));
|
||||
}
|
||||
};
|
||||
|
||||
template<class K, class T, class Compare>
|
||||
class flat_map<K,T,Compare>::value_compare {
|
||||
friend class flat_map;
|
||||
public:
|
||||
|
||||
bool operator() (const value_type& x, const value_type& y) const {
|
||||
return comp(x.first, y.first);
|
||||
}
|
||||
|
||||
//For std::lower_bound/std::upper_bound
|
||||
bool operator() (const value_type& x, const key_type& y) const {
|
||||
return comp(x.first, y);
|
||||
}
|
||||
private:
|
||||
value_compare(Compare c) : comp(c) {}
|
||||
|
||||
Compare comp;
|
||||
};
|
||||
|
||||
} //namespace
|
||||
#endif
|
|
@ -0,0 +1,167 @@
|
|||
#ifndef VTR_GEOMETRY_H
|
||||
#define VTR_GEOMETRY_H
|
||||
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include <limits>
|
||||
|
||||
#include "vtr_range.h"
|
||||
|
||||
namespace vtr {
|
||||
|
||||
/*
|
||||
* Forward declarations
|
||||
*/
|
||||
template<class T>
|
||||
class Point;
|
||||
|
||||
template<class T>
|
||||
class Rect;
|
||||
|
||||
template<class T>
|
||||
class Line;
|
||||
|
||||
template<class T>
|
||||
class RectUnion;
|
||||
|
||||
template<class T>
|
||||
bool operator==(Point<T> lhs, Point<T> rhs);
|
||||
template<class T>
|
||||
bool operator!=(Point<T> lhs, Point<T> rhs);
|
||||
template<class T>
|
||||
bool operator<(Point<T> lhs, Point<T> rhs);
|
||||
|
||||
template<class T>
|
||||
bool operator==(const Rect<T>& lhs, const Rect<T>& rhs);
|
||||
template<class T>
|
||||
bool operator!=(const Rect<T>& lhs, const Rect<T>& rhs);
|
||||
|
||||
template<class T>
|
||||
bool operator==(const RectUnion<T>& lhs, const RectUnion<T>& rhs);
|
||||
template<class T>
|
||||
bool operator!=(const RectUnion<T>& lhs, const RectUnion<T>& rhs);
|
||||
/*
|
||||
* Class Definitions
|
||||
*/
|
||||
|
||||
//A point in 2D space
|
||||
template<class T>
|
||||
class Point {
|
||||
public: //Constructors
|
||||
Point(T x_val, T y_val);
|
||||
|
||||
public: //Accessors
|
||||
|
||||
//Coordinates
|
||||
T x() const;
|
||||
T y() const;
|
||||
|
||||
friend bool operator== <>(Point<T> lhs, Point<T> rhs);
|
||||
friend bool operator!= <>(Point<T> lhs, Point<T> rhs);
|
||||
friend bool operator< <>(Point<T> lhs, Point<T> rhs);
|
||||
private:
|
||||
T x_;
|
||||
T y_;
|
||||
};
|
||||
|
||||
//A 2D rectangle
|
||||
template<class T>
|
||||
class Rect {
|
||||
public: //Constructors
|
||||
Rect(T left_val, T bottom_val, T right_val, T top_val);
|
||||
Rect(Point<T> bottom_left_val, Point<T> top_right_val);
|
||||
Rect(); /* For RRGraph obj */
|
||||
|
||||
public: //Accessors
|
||||
//Co-ordinates
|
||||
T xmin() const;
|
||||
T xmax() const;
|
||||
T ymin() const;
|
||||
T ymax() const;
|
||||
|
||||
Point<T> bottom_left() const;
|
||||
Point<T> top_right() const;
|
||||
|
||||
T width() const;
|
||||
T height() const;
|
||||
|
||||
//Returns true if the point is fully contained within the rectangle (excluding the top-right edges)
|
||||
bool contains(Point<T> point) const;
|
||||
|
||||
//Returns true if the point is strictly contained within the region (excluding all edges)
|
||||
bool strictly_contains(Point<T> point) const;
|
||||
|
||||
//Returns true if the point is coincident with the rectangle (including the top-right edges)
|
||||
bool coincident(Point<T> point) const;
|
||||
|
||||
friend bool operator== <>(const Rect<T>& lhs, const Rect<T>& rhs);
|
||||
friend bool operator!= <>(const Rect<T>& lhs, const Rect<T>& rhs);
|
||||
|
||||
private:
|
||||
Point<T> bottom_left_;
|
||||
Point<T> top_right_;
|
||||
};
|
||||
|
||||
//A 2D line
|
||||
template<class T>
|
||||
class Line {
|
||||
public: //Types
|
||||
typedef typename std::vector<Point<T>>::const_iterator point_iter;
|
||||
typedef vtr::Range<point_iter> point_range;
|
||||
|
||||
public: //Constructors
|
||||
Line(std::vector<Point<T>> line_points);
|
||||
|
||||
public: //Accessors
|
||||
|
||||
//Returns the bounding box
|
||||
Rect<T> bounding_box() const;
|
||||
|
||||
//Returns a range of constituent points
|
||||
point_range points() const;
|
||||
|
||||
private:
|
||||
std::vector<Point<T>> points_;
|
||||
};
|
||||
|
||||
//A union of 2d rectangles
|
||||
template<class T>
|
||||
class RectUnion {
|
||||
public: //Types
|
||||
typedef typename std::vector<Rect<T>>::const_iterator rect_iter;
|
||||
typedef vtr::Range<rect_iter> rect_range;
|
||||
|
||||
public: //Constructors
|
||||
|
||||
//Construct from a set of rectangles
|
||||
RectUnion(std::vector<Rect<T>> rects);
|
||||
|
||||
public: //Accessors
|
||||
//Returns the bounding box of all rectangles in the union
|
||||
Rect<T> bounding_box() const;
|
||||
|
||||
//Returns true if the point is fully contained within the region (excluding top-right edges)
|
||||
bool contains(Point<T> point) const;
|
||||
|
||||
//Returns true if the point is strictly contained within the region (excluding all edges)
|
||||
bool strictly_contains(Point<T> point) const;
|
||||
|
||||
//Returns true if the point is coincident with the region (including the top-right edges)
|
||||
bool coincident(Point<T> point) const;
|
||||
|
||||
//Returns a range of all constituent rectangles
|
||||
rect_range rects() const;
|
||||
|
||||
//Checks whether two RectUnions have identical representations
|
||||
// Note: does not check whether the representations they are equivalent
|
||||
friend bool operator== <>(const RectUnion<T>& lhs, const RectUnion<T>& rhs);
|
||||
friend bool operator!= <>(const RectUnion<T>& lhs, const RectUnion<T>& rhs);
|
||||
private:
|
||||
//Note that a union of rectanges may have holes and may not be contiguous
|
||||
std::vector<Rect<T>> rects_;
|
||||
};
|
||||
|
||||
} //namespace
|
||||
|
||||
#include "vtr_geometry.tpp"
|
||||
#endif
|
|
@ -0,0 +1,248 @@
|
|||
namespace vtr {
|
||||
/*
|
||||
* Point
|
||||
*/
|
||||
|
||||
template<class T>
|
||||
Point<T>::Point(T x_val, T y_val)
|
||||
: x_(x_val)
|
||||
, y_(y_val) {
|
||||
//pass
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T Point<T>::x() const {
|
||||
return x_;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T Point<T>::y() const {
|
||||
return y_;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool operator==(Point<T> lhs, Point<T> rhs) {
|
||||
return lhs.x() == rhs.x()
|
||||
&& lhs.y() == rhs.y();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool operator!=(Point<T> lhs, Point<T> rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool operator<(Point<T> lhs, Point<T> rhs) {
|
||||
return std::make_tuple(lhs.x(), lhs.y()) < std::make_tuple(rhs.x(), rhs.y());
|
||||
}
|
||||
|
||||
/*
|
||||
* Rect
|
||||
*/
|
||||
template<class T>
|
||||
Rect<T>::Rect(T left_val, T bottom_val, T right_val, T top_val)
|
||||
: Rect<T>(Point<T>(left_val, bottom_val), Point<T>(right_val, top_val)) {
|
||||
//pass
|
||||
}
|
||||
|
||||
template<class T>
|
||||
Rect<T>::Rect(Point<T> bottom_left_val, Point<T> top_right_val)
|
||||
: bottom_left_(bottom_left_val)
|
||||
, top_right_(top_right_val) {
|
||||
//pass
|
||||
}
|
||||
|
||||
/* A new contructor which initialize everything to be zero*/
|
||||
template<class T>
|
||||
Rect<T>::Rect()
|
||||
: Rect<T>(Point<T>(0, 0), Point<T>(0, 0)) {
|
||||
//pass
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T Rect<T>::xmin() const {
|
||||
return bottom_left_.x();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T Rect<T>::xmax() const {
|
||||
return top_right_.x();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T Rect<T>::ymin() const {
|
||||
return bottom_left_.y();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T Rect<T>::ymax() const {
|
||||
return top_right_.y();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
Point<T> Rect<T>::bottom_left() const {
|
||||
return bottom_left_;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
Point<T> Rect<T>::top_right() const {
|
||||
return top_right_;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T Rect<T>::width() const {
|
||||
return xmax() - xmin();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T Rect<T>::height() const {
|
||||
return ymax() - ymin();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool Rect<T>::contains(Point<T> point) const {
|
||||
//Up-to but not including right or top edges
|
||||
return point.x() >= xmin() && point.x() < xmax()
|
||||
&& point.y() >= ymin() && point.y() < ymax();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool Rect<T>::strictly_contains(Point<T> point) const {
|
||||
//Excluding edges
|
||||
return point.x() > xmin() && point.x() < xmax()
|
||||
&& point.y() > ymin() && point.y() < ymax();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool Rect<T>::coincident(Point<T> point) const {
|
||||
//Including right or top edges
|
||||
return point.x() >= xmin() && point.x() <= xmax()
|
||||
&& point.y() >= ymin() && point.y() <= ymax();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool operator==(const Rect<T>& lhs, const Rect<T>& rhs) {
|
||||
return lhs.bottom_left() == rhs.bottom_left()
|
||||
&& lhs.top_right() == rhs.top_right();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool operator!=(const Rect<T>& lhs, const Rect<T>& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Line
|
||||
*/
|
||||
template<class T>
|
||||
Line<T>::Line(std::vector<Point<T>> line_points)
|
||||
: points_(line_points) {
|
||||
//pass
|
||||
}
|
||||
|
||||
template<class T>
|
||||
Rect<T> Line<T>::bounding_box() const {
|
||||
T xmin = std::numeric_limits<T>::max();
|
||||
T ymin = std::numeric_limits<T>::max();
|
||||
T xmax = std::numeric_limits<T>::min();
|
||||
T ymax = std::numeric_limits<T>::min();
|
||||
|
||||
for (const auto& point : points()) {
|
||||
xmin = std::min(xmin, point.x());
|
||||
ymin = std::min(ymin, point.y());
|
||||
xmax = std::max(xmax, point.x());
|
||||
ymax = std::max(ymax, point.y());
|
||||
}
|
||||
|
||||
return Rect<T>(xmin, ymin, xmax, ymax);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename Line<T>::point_range Line<T>::points() const {
|
||||
return vtr::make_range(points_.begin(), points_.end());
|
||||
}
|
||||
|
||||
/*
|
||||
* RectUnion
|
||||
*/
|
||||
template<class T>
|
||||
RectUnion<T>::RectUnion(std::vector<Rect<T>> rectangles)
|
||||
: rects_(rectangles) {
|
||||
//pass
|
||||
}
|
||||
|
||||
template<class T>
|
||||
Rect<T> RectUnion<T>::bounding_box() const {
|
||||
T xmin = std::numeric_limits<T>::max();
|
||||
T ymin = std::numeric_limits<T>::max();
|
||||
T xmax = std::numeric_limits<T>::min();
|
||||
T ymax = std::numeric_limits<T>::min();
|
||||
|
||||
for (const auto& rect : rects_) {
|
||||
xmin = std::min(xmin, rect.xmin());
|
||||
ymin = std::min(ymin, rect.ymin());
|
||||
xmax = std::max(xmax, rect.xmax());
|
||||
ymax = std::max(ymax, rect.ymax());
|
||||
}
|
||||
|
||||
return Rect<T>(xmin, ymin, xmax, ymax);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool RectUnion<T>::contains(Point<T> point) const {
|
||||
for(const auto& rect : rects()) {
|
||||
if (rect.contains(point)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool RectUnion<T>::strictly_contains(Point<T> point) const {
|
||||
for(const auto& rect : rects()) {
|
||||
if (rect.strictly_contains(point)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool RectUnion<T>::coincident(Point<T> point) const {
|
||||
for(const auto& rect : rects()) {
|
||||
if (rect.coincident(point)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename RectUnion<T>::rect_range RectUnion<T>::rects() const {
|
||||
return vtr::make_range(rects_.begin(), rects_.end());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool operator==(const RectUnion<T>& lhs, const RectUnion<T>& rhs) {
|
||||
//Currently checks for an identical *representation* (not whether the
|
||||
//representations are equivalent)
|
||||
|
||||
if (lhs.rects_.size() != rhs.rects_.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < lhs.rects_.size(); ++i) {
|
||||
if (lhs.rects_[i] != rhs.rects_[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool operator!=(const RectUnion<T>& lhs, const RectUnion<T>& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
} //namespace
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef VTR_HASH_H
|
||||
#define VTR_HASH_H
|
||||
#include <functional>
|
||||
|
||||
namespace vtr {
|
||||
|
||||
//Hashes v and combines it with seed (as in boost)
|
||||
//
|
||||
//This is typically used to implement std::hash for composite types.
|
||||
template <class T>
|
||||
inline void hash_combine(std::size_t& seed, const T& v)
|
||||
{
|
||||
std::hash<T> hasher;
|
||||
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
|
||||
}
|
||||
|
||||
} //namespace
|
||||
|
||||
#endif
|
|
@ -0,0 +1,263 @@
|
|||
#ifndef VTR_LINEAR_MAP_H
|
||||
#define VTR_LINEAR_MAP_H
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "vtr_sentinels.h"
|
||||
|
||||
namespace vtr {
|
||||
|
||||
//A std::map-like container which is indexed by K
|
||||
//
|
||||
//The main use of this container is to behave like a std::map which is optimized to hold
|
||||
//mappings between a dense linear range of keys (e.g. vtr::StrongId).
|
||||
//
|
||||
//Requires that K be convertable to size_t with the size_t operator (i.e. size_t()), and
|
||||
//that the conversion results in a linearly increasing index into the underlying vector.
|
||||
//Also requires that K() return the sentinel value used to mark invalid entries.
|
||||
//
|
||||
//If you only need to access the value associated with the key consider using vtr::vector_map
|
||||
//instead, which provides a similar but more std::vector-like interface.
|
||||
//
|
||||
//Note that it is possible to use linear_map with sparse/non-contiguous keys, but this is typically
|
||||
//memory inefficient as the underlying vector will allocate space for [0..size_t(max_key)-1],
|
||||
//where max_key is the largest key that has been inserted.
|
||||
//
|
||||
//As with a std::vector, it is the caller's responsibility to ensure there is sufficient space
|
||||
//when a given index/key before it is accessed. The exception to this are the find() and insert()
|
||||
//methods which handle non-existing keys gracefully.
|
||||
template<class K, class T, class Sentinel=DefaultSentinel<K>>
|
||||
class linear_map {
|
||||
public:
|
||||
typedef K key_type;
|
||||
typedef T mapped_type;
|
||||
typedef std::pair<K,T> value_type;
|
||||
typedef value_type& reference;
|
||||
typedef const value_type& const_reference;
|
||||
typedef typename std::vector<value_type>::iterator iterator;
|
||||
typedef typename std::vector<value_type>::const_iterator const_iterator;
|
||||
typedef typename std::vector<value_type>::reverse_iterator reverse_iterator;
|
||||
typedef typename std::vector<value_type>::const_reverse_iterator const_reverse_iterator;
|
||||
typedef typename std::vector<value_type>::difference_type difference_type;
|
||||
typedef typename std::vector<value_type>::size_type size_type;
|
||||
|
||||
public:
|
||||
//Standard big 5
|
||||
linear_map() = default;
|
||||
linear_map(const linear_map&) = default;
|
||||
linear_map(linear_map&&) = default;
|
||||
linear_map& operator=(const linear_map&) = default;
|
||||
linear_map& operator=(linear_map&&) = default;
|
||||
|
||||
linear_map(size_t num_keys)
|
||||
: vec_(num_keys, std::make_pair(sentinel(), T())) //Initialize all with sentinel values
|
||||
{}
|
||||
|
||||
iterator begin() { return vec_.begin(); }
|
||||
const_iterator begin() const { return vec_.begin(); }
|
||||
iterator end() { return vec_.end(); }
|
||||
const_iterator end() const { return vec_.end(); }
|
||||
reverse_iterator rbegin() { return vec_.rbegin(); }
|
||||
const_reverse_iterator rbegin() const { return vec_.rbegin(); }
|
||||
reverse_iterator rend() { return vec_.rend(); }
|
||||
const_reverse_iterator rend() const { return vec_.rend(); }
|
||||
const_iterator cbegin() const { return vec_.begin(); }
|
||||
const_iterator cend() const { return vec_.end(); }
|
||||
const_reverse_iterator crbegin() const { return vec_.rbegin(); }
|
||||
const_reverse_iterator crend() const { return vec_.rend(); }
|
||||
|
||||
bool empty() const { return vec_.empty(); }
|
||||
size_type size() const { return vec_.size(); }
|
||||
size_type max_size() const { return vec_.max_size(); }
|
||||
|
||||
mapped_type& operator[](const key_type& key) {
|
||||
auto iter = find(key);
|
||||
if(iter == end()) {
|
||||
//Not found, create it
|
||||
iter = insert(std::make_pair(key,mapped_type())).first;
|
||||
}
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
mapped_type& at(const key_type& key) {
|
||||
return const_cast<mapped_type&>(const_cast<const linear_map*>(this)->at(key));
|
||||
}
|
||||
|
||||
const mapped_type& at(const key_type& key) const {
|
||||
auto iter = find(key);
|
||||
if(iter == end()) {
|
||||
throw std::out_of_range("Invalid key");
|
||||
}
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
//Insert value
|
||||
std::pair<iterator,bool> insert(const value_type& value) {
|
||||
auto iter = find(value.first);
|
||||
if(iter != end()) {
|
||||
//Found existing
|
||||
return std::make_pair(iter, false);
|
||||
} else {
|
||||
//Insert
|
||||
size_t index = size_t(value.first);
|
||||
|
||||
if(index >= vec_.size()) {
|
||||
//Make space, initialize empty slots with sentinel values
|
||||
vec_.resize(index + 1, std::make_pair(sentinel(), T()));
|
||||
}
|
||||
|
||||
vec_[index] = value;
|
||||
|
||||
return std::make_pair(vec_.begin() + index, true);
|
||||
}
|
||||
}
|
||||
|
||||
//Insert range
|
||||
template<class InputIterator>
|
||||
void insert(InputIterator first, InputIterator last) {
|
||||
for(InputIterator iter = first; iter != last; ++iter) {
|
||||
insert(*iter);
|
||||
}
|
||||
}
|
||||
|
||||
//Erase by key
|
||||
void erase(const key_type& key) {
|
||||
auto iter = find(key);
|
||||
if(iter != end()) {
|
||||
erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
//Erase at iterator
|
||||
void erase(const_iterator position) {
|
||||
iterator pos = convert_to_iterator(position);
|
||||
pos->first = sentinel(); //Mark invalid
|
||||
}
|
||||
|
||||
//Erase range
|
||||
void erase(const_iterator first, const_iterator last) {
|
||||
for(auto iter = first; iter != last; ++iter) {
|
||||
erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void swap(linear_map& other) { std::swap(vec_, other.vec_); }
|
||||
|
||||
void clear() { vec_.clear(); }
|
||||
|
||||
template<class ...Args>
|
||||
std::pair<iterator,bool> emplace(const key_type& key, Args&&... args) {
|
||||
auto iter = find(key);
|
||||
if(iter != end()) {
|
||||
//Found
|
||||
return std::make_pair(iter, false);
|
||||
} else {
|
||||
//Emplace
|
||||
size_t index = size_t(key);
|
||||
|
||||
if(index >= vec_.size()) {
|
||||
//Make space, initialize empty slots with sentinel values
|
||||
vec_.resize(index + 1, value_type(sentinel(), T()));
|
||||
}
|
||||
|
||||
vec_[index] = value_type(key, std::forward<Args>(args)...);
|
||||
|
||||
return std::make_pair(vec_.begin() + index, true);
|
||||
}
|
||||
}
|
||||
|
||||
void reserve(size_type n) { vec_.reserve(n); }
|
||||
void shrink_to_fit() { vec_.shrink_to_fit(); }
|
||||
|
||||
iterator find(const key_type& key) {
|
||||
const_iterator const_iter = const_cast<const linear_map*>(this)->find(key);
|
||||
return convert_to_iterator(const_iter);
|
||||
}
|
||||
|
||||
const_iterator find(const key_type& key) const {
|
||||
size_t index = size_t(key);
|
||||
|
||||
if(index < vec_.size() && vec_[index].first != sentinel()) {
|
||||
return vec_.begin() + index;
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
size_type count(const key_type& key) const {
|
||||
return (find(key) == end()) ? 0 : 1;
|
||||
}
|
||||
|
||||
iterator lower_bound(const key_type& key) {
|
||||
const_iterator const_iter = const_cast<const linear_map*>(this)->lower_bound(key);
|
||||
return convert_to_iterator(const_iter);
|
||||
}
|
||||
|
||||
const_iterator lower_bound(const key_type& key) const {
|
||||
return find(key);
|
||||
}
|
||||
|
||||
iterator upper_bound(const key_type& key) {
|
||||
const_iterator const_iter = const_cast<const linear_map*>(this)->upper_bound(key);
|
||||
return convert_to_iterator(const_iter);
|
||||
}
|
||||
|
||||
const_iterator upper_bound(const key_type& key) const {
|
||||
auto iter = find(key);
|
||||
return (iter != end()) ? iter + 1 : end();
|
||||
}
|
||||
|
||||
std::pair<iterator,iterator> equal_range(const key_type& key) {
|
||||
auto const_iter_pair = const_cast<const linear_map*>(this)->equal_range(key);
|
||||
return std::pair<iterator,iterator>(iterator(const_iter_pair.first), iterator(const_iter_pair.second));
|
||||
}
|
||||
|
||||
std::pair<const_iterator,const_iterator> equal_range(const key_type& key) const {
|
||||
auto lb_iter = lower_bound(key);
|
||||
auto ub_iter = upper_bound(key);
|
||||
return (lb_iter != end()) ? std::make_pair(lb_iter, ub_iter) : std::make_pair(ub_iter, ub_iter);
|
||||
}
|
||||
|
||||
size_type valid_size() const {
|
||||
size_t valid_cnt = 0;
|
||||
for(const auto& kv : vec_) {
|
||||
if(kv.first != sentinel()) {
|
||||
++valid_cnt;
|
||||
}
|
||||
}
|
||||
return valid_cnt;
|
||||
}
|
||||
|
||||
public:
|
||||
friend void swap(linear_map& lhs, linear_map& rhs) {
|
||||
std::swap(lhs.vec_, rhs.vec_);
|
||||
}
|
||||
|
||||
private:
|
||||
iterator convert_to_iterator(const_iterator const_iter) {
|
||||
//This is a work around for the fact that there is no conversion between
|
||||
//a const_iterator and iterator.
|
||||
//
|
||||
//We intiailize i to the start of the container and then advance it by
|
||||
//the distance to const_iter. The resulting i points to the same element
|
||||
//as const_iter
|
||||
//
|
||||
//Note that to be able to call std::distance with an iterator and
|
||||
//const_iterator we need to specify the type as const_iterator (relying
|
||||
//on the implicit conversion from iterator to const_iterator for i)
|
||||
//
|
||||
//Since the iterators are really vector (i.e. random-access) iterators
|
||||
//both distance and advance take constant time
|
||||
iterator i = begin();
|
||||
std::advance(i, std::distance<const_iterator>(i, const_iter));
|
||||
return i;
|
||||
}
|
||||
|
||||
constexpr K sentinel() const { return Sentinel::INVALID(); }
|
||||
|
||||
private:
|
||||
std::vector<value_type> vec_;
|
||||
};
|
||||
|
||||
} //namespace
|
||||
#endif
|
|
@ -0,0 +1,34 @@
|
|||
#include <cstdlib>
|
||||
|
||||
#include "vtr_list.h"
|
||||
#include "vtr_memory.h"
|
||||
|
||||
namespace vtr {
|
||||
|
||||
t_linked_vptr *insert_in_vptr_list(t_linked_vptr *head, void *vptr_to_add) {
|
||||
|
||||
/* Inserts a new element at the head of a linked list of void pointers. *
|
||||
* Returns the new head of the list. */
|
||||
|
||||
t_linked_vptr *linked_vptr;
|
||||
|
||||
linked_vptr = (t_linked_vptr *) vtr::malloc(sizeof(t_linked_vptr));
|
||||
|
||||
linked_vptr->data_vptr = vptr_to_add;
|
||||
linked_vptr->next = head;
|
||||
return (linked_vptr); /* New head of the list */
|
||||
}
|
||||
|
||||
/* Deletes the element at the head of a linked list of void pointers. *
|
||||
* Returns the new head of the list. */
|
||||
t_linked_vptr *delete_in_vptr_list(t_linked_vptr *head) {
|
||||
t_linked_vptr *linked_vptr;
|
||||
|
||||
if (head == nullptr )
|
||||
return nullptr ;
|
||||
linked_vptr = head->next;
|
||||
free(head);
|
||||
return linked_vptr; /* New head of the list */
|
||||
}
|
||||
|
||||
} //namespace
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef VTR_LIST_H
|
||||
#define VTR_LIST_H
|
||||
|
||||
namespace vtr {
|
||||
/* Linked lists of void pointers and integers, respectively. */
|
||||
|
||||
struct t_linked_vptr {
|
||||
void *data_vptr;
|
||||
struct t_linked_vptr *next;
|
||||
};
|
||||
|
||||
t_linked_vptr *insert_in_vptr_list(t_linked_vptr *head,
|
||||
void *vptr_to_add);
|
||||
t_linked_vptr *delete_in_vptr_list(t_linked_vptr *head);
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,17 @@
|
|||
#include "vtr_log.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
namespace vtr {
|
||||
PrintHandlerInfo printf = log_print_info;
|
||||
PrintHandlerInfo printf_info = log_print_info;
|
||||
PrintHandlerWarning printf_warning = log_print_warning;
|
||||
PrintHandlerError printf_error = log_print_error;
|
||||
PrintHandlerDirect printf_direct = log_print_direct;
|
||||
|
||||
|
||||
void set_log_file(const char* filename) {
|
||||
log_set_output_file(filename);
|
||||
}
|
||||
|
||||
} //namespace
|
|
@ -0,0 +1,133 @@
|
|||
#ifndef VTR_LOG_H
|
||||
#define VTR_LOG_H
|
||||
#include <tuple>
|
||||
|
||||
/*
|
||||
* This header defines useful logging macros for VTR projects.
|
||||
*
|
||||
* Message Type
|
||||
* ============
|
||||
*
|
||||
* Three types of log message types are defined:
|
||||
* VTR_LOG - The standard 'info' type log message
|
||||
* VTR_LOG_WARN - A warning log message
|
||||
* VTR_LOG_ERROR - An error log message
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* VTR_LOG("This produces a regular '%s' message\n", "info");
|
||||
* VTR_LOG_WARN("This produces a '%s' message\n", "warning");
|
||||
* VTR_LOG_ERROR("This produces an '%s' message\n", "error");
|
||||
*
|
||||
* Conditional Logging
|
||||
* ===================
|
||||
*
|
||||
* Each of the three message types also have a VTR_LOGV_* variant,
|
||||
* which will cause the message to be logged if a user-defined condition
|
||||
* is satisifed.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* VTR_LOGV(verbosity > 5, "This message will be logged only if verbosity is greater than %d\n", 5);
|
||||
* VTR_LOGV_WARN(verbose, "This warning message will be logged if verbose is true\n");
|
||||
* VTR_LOGV_ERROR(false, "This error message will never be logged\n");
|
||||
*
|
||||
* Custom Location Logging
|
||||
* =======================
|
||||
*
|
||||
* Each of the three message types also have a VTR_LOGF_* variant,
|
||||
* which will cause the message to be logged for a custom file and
|
||||
* line loation.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* VTR_LOGF("my_file.txt", 42 "This message will be logged from file 'my_file.txt' line %d\n", 42);
|
||||
*
|
||||
* Debug Logging
|
||||
* =============
|
||||
*
|
||||
* For debug purposes it may be useful to have additional logging.
|
||||
* This is supported by VTR_LOG_DEBUG() and VTR_LOGV_DEBUG().
|
||||
*
|
||||
* To avoid run-time overhead, these are only enabled if VTR_ENABLE_DEBUG_LOGGING
|
||||
* is defined (disabled by default).
|
||||
*/
|
||||
|
||||
//Unconditional logging macros
|
||||
#define VTR_LOG(...) VTR_LOGV(true, __VA_ARGS__)
|
||||
#define VTR_LOG_WARN(...) VTR_LOGV_WARN(true, __VA_ARGS__)
|
||||
#define VTR_LOG_ERROR(...) VTR_LOGV_ERROR(true, __VA_ARGS__)
|
||||
#define VTR_LOG_NOP(...) VTR_LOGV_NOP(true, __VA_ARGS__)
|
||||
|
||||
//Conditional logging macros
|
||||
#define VTR_LOGV(expr, ...) VTR_LOGVF(expr, __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define VTR_LOGV_WARN(expr, ...) VTR_LOGVF_WARN(expr, __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define VTR_LOGV_ERROR(expr, ...) VTR_LOGVF_ERROR(expr, __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define VTR_LOGV_NOP(expr, ...) VTR_LOGVF_NOP(expr, __FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
//Custom file-line location logging macros
|
||||
#define VTR_LOGF(file, line, ...) VTR_LOGVF(true, file, line, __VA_ARGS__)
|
||||
#define VTR_LOGF_WARN(file, line, ...) VTR_LOGVF_WARN(true, file, line, __VA_ARGS__)
|
||||
#define VTR_LOGF_ERROR(file, line, ...) VTR_LOGVF_ERROR(true, file, line, __VA_ARGS__)
|
||||
#define VTR_LOGF_NOP(file, line, ...) VTR_LOGVF_NOP(true, file, line, __VA_ARGS__)
|
||||
|
||||
//Conditional logging and custom file-line location macros
|
||||
#define VTR_LOGVF(expr, file, line, ...) \
|
||||
do { \
|
||||
if (expr) vtr::printf(__VA_ARGS__); \
|
||||
} while (false)
|
||||
|
||||
#define VTR_LOGVF_WARN(expr, file, line, ...) \
|
||||
do { \
|
||||
if (expr) vtr::printf_warning(file, line, __VA_ARGS__); \
|
||||
} while (false)
|
||||
|
||||
#define VTR_LOGVF_ERROR(expr, file, line, ...) \
|
||||
do { \
|
||||
if (expr) vtr::printf_error(file, line, __VA_ARGS__); \
|
||||
} while (false)
|
||||
|
||||
//No-op version of logging macro which avoids unused parameter warnings.
|
||||
//
|
||||
//Note that to avoid unused parameter warnings we call sizeof() and cast
|
||||
//the result to void. sizeof is evaluated at compile time so there is no
|
||||
//run-time overhead.
|
||||
//
|
||||
//Also note the use of std::make_tuple to ensure all arguments in VA_ARGS
|
||||
//are used.
|
||||
#define VTR_LOGVF_NOP(expr, file, line, ...) \
|
||||
do { \
|
||||
static_cast<void>(sizeof(expr)); \
|
||||
static_cast<void>(sizeof(file)); \
|
||||
static_cast<void>(sizeof(line)); \
|
||||
static_cast<void>(sizeof(std::make_tuple(__VA_ARGS__))); \
|
||||
} while (false)
|
||||
|
||||
//Debug logging macros
|
||||
#ifdef VTR_ENABLE_DEBUG_LOGGING //Enable
|
||||
# define VTR_LOG_DEBUG(...) VTR_LOG(__VA_ARGS__)
|
||||
# define VTR_LOGV_DEBUG(expr, ...) VTR_LOGV(expr, __VA_ARGS__)
|
||||
#else //Disable
|
||||
# define VTR_LOG_DEBUG(...) VTR_LOG_NOP(__VA_ARGS__)
|
||||
# define VTR_LOGV_DEBUG(expr, ...) VTR_LOGV_NOP(expr, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
namespace vtr {
|
||||
|
||||
typedef void (*PrintHandlerInfo)(const char* pszMessage, ... );
|
||||
typedef void (*PrintHandlerWarning)(const char* pszFileName, unsigned int lineNum, const char* pszMessage, ... );
|
||||
typedef void (*PrintHandlerError)(const char* pszFileName, unsigned int lineNum, const char* pszMessage, ... );
|
||||
typedef void (*PrintHandlerDirect)(const char* pszMessage, ... );
|
||||
|
||||
extern PrintHandlerInfo printf; //Same as printf_info
|
||||
extern PrintHandlerInfo printf_info;
|
||||
extern PrintHandlerWarning printf_warning;
|
||||
extern PrintHandlerError printf_error;
|
||||
extern PrintHandlerDirect printf_direct;
|
||||
|
||||
void set_log_file(const char* filename);
|
||||
|
||||
} //namespace
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef VTR_LOGIC_H
|
||||
#define VTR_LOGIC_H
|
||||
|
||||
namespace vtr {
|
||||
|
||||
enum class LogicValue {
|
||||
FALSE = 0,
|
||||
TRUE = 1,
|
||||
DONT_CARE = 2,
|
||||
UNKOWN = 3
|
||||
};
|
||||
|
||||
} //namespace
|
||||
|
||||
#endif
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef VTR_MAP_UTIL_H
|
||||
#define VTR_MAP_UTIL_H
|
||||
|
||||
#include "vtr_pair_util.h"
|
||||
#include "vtr_range.h"
|
||||
|
||||
namespace vtr {
|
||||
|
||||
//An iterator who wraps a std::map iterator to return it's key
|
||||
template<typename Iter>
|
||||
using map_key_iter = pair_first_iter<Iter>;
|
||||
|
||||
//An iterator who wraps a std::map iterator to return it's value
|
||||
template<typename Iter>
|
||||
using map_value_iter = pair_second_iter<Iter>;
|
||||
|
||||
//Returns a range iterating over a std::map's keys
|
||||
template<typename T>
|
||||
auto make_key_range(T b, T e) {
|
||||
using key_iter = map_key_iter<T>;
|
||||
return vtr::make_range(key_iter(b), key_iter(e));
|
||||
}
|
||||
|
||||
//Returns a range iterating over a std::map's keys
|
||||
template<typename Container>
|
||||
auto make_key_range(const Container& c) {
|
||||
return make_key_range(std::begin(c), std::end(c));
|
||||
}
|
||||
|
||||
//Returns a range iterating over a std::map's values
|
||||
template<typename T>
|
||||
auto make_value_range(T b, T e) {
|
||||
using value_iter = map_value_iter<T>;
|
||||
return vtr::make_range(value_iter(b), value_iter(e));
|
||||
}
|
||||
|
||||
//Returns a range iterating over a std::map's values
|
||||
template<typename Container>
|
||||
auto make_value_range(const Container& c) {
|
||||
return make_value_range(std::begin(c), std::end(c));
|
||||
}
|
||||
|
||||
} //namespace
|
||||
|
||||
#endif
|
|
@ -0,0 +1,86 @@
|
|||
#include <map>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_error.h"
|
||||
#include "vtr_math.h"
|
||||
|
||||
namespace vtr {
|
||||
|
||||
int ipow(int base, int exp) {
|
||||
int result = 1;
|
||||
|
||||
VTR_ASSERT(exp >= 0);
|
||||
|
||||
while (exp) {
|
||||
if (exp & 1)
|
||||
result *= base;
|
||||
exp >>= 1;
|
||||
base *= base;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Performs linear interpolation or extrapolation on the set of (x,y) values specified by the xy_map.
|
||||
A requested x value is passed in, and we return the interpolated/extrapolated y value at this requested value of x.
|
||||
Meant for maps where both key and element are numbers.
|
||||
This is specifically enforced by the explicit instantiations below this function. i.e. only templates
|
||||
using those types listed in the explicit instantiations below are allowed */
|
||||
template<typename X, typename Y> Y linear_interpolate_or_extrapolate(const std::map<X,Y> *xy_map, X requested_x){
|
||||
Y result;
|
||||
|
||||
/* the intention of this function is to interpolate/extrapolate. we can't do so with less than 2 values in the xy_map */
|
||||
if (xy_map->size() < 2){
|
||||
throw VtrError("linear_interpolate_or_extrapolate: cannot interpolate/extrapolate based on less than 2 (x,y) pairs", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
auto itr = xy_map->find(requested_x);
|
||||
if (itr != xy_map->end()){
|
||||
/* requested x already exists in the x,y map */
|
||||
result = itr->second;
|
||||
} else {
|
||||
/* requested x does not exist in the x,y map. need to interpolate/extrapolate */
|
||||
|
||||
typename std::map<X,Y>::const_iterator it;
|
||||
double x_low, x_high, y_low, y_high;
|
||||
double slope, reference_y, delta_x;
|
||||
|
||||
/* get first x greater than the one requested */
|
||||
it = xy_map->upper_bound(requested_x);
|
||||
|
||||
if(it == xy_map->end()){
|
||||
/* need to extrapolate to higher x. based on the y values at the two largest x values */
|
||||
it--;
|
||||
x_high = (double)it->first;
|
||||
y_high = (double)it->second;
|
||||
it--;
|
||||
x_low = (double)it->first;
|
||||
y_low = (double)it->second;
|
||||
} else if (it == xy_map->begin()){
|
||||
/* need to extrapolate to lower x. based on the y values at the two smallest x */
|
||||
x_low = (double)it->first;
|
||||
y_low = (double)it->second;
|
||||
it++;
|
||||
x_high = (double)it->first;
|
||||
y_high = (double)it->second;
|
||||
} else {
|
||||
/* need to interpolate. based on y values at x just above/below
|
||||
the one we want */
|
||||
x_high = (double)it->first;
|
||||
y_high = (double)it->second;
|
||||
it--;
|
||||
x_low = (double)it->first;
|
||||
y_low = (double)it->second;
|
||||
}
|
||||
|
||||
slope = (y_high - y_low) / (x_high - x_low);
|
||||
reference_y = y_low;
|
||||
delta_x = (double)requested_x - x_low;
|
||||
result = (Y)(reference_y + (slope * delta_x));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
template double linear_interpolate_or_extrapolate(const std::map<int,double> *xy_map, int requested_x); /* (int,double) */
|
||||
template double linear_interpolate_or_extrapolate(const std::map<double,double> *xy_map, double requested_x); /* (double,double) */
|
||||
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
#ifndef VTR_MATH_H
|
||||
#define VTR_MATH_H
|
||||
|
||||
#include <map>
|
||||
#include <cmath>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
|
||||
namespace vtr {
|
||||
/*********************** Math operations *************************************/
|
||||
int ipow(int base, int exp);
|
||||
|
||||
template<typename X, typename Y>
|
||||
Y linear_interpolate_or_extrapolate(const std::map<X,Y> *xy_map, X requested_x);
|
||||
|
||||
constexpr int nint(float val) { return static_cast<int>(val + 0.5); }
|
||||
|
||||
//Returns a 'safe' ratio which evaluates to zero if the denominator is zero
|
||||
template<typename T>
|
||||
T safe_ratio(T numerator, T denominator) {
|
||||
if (denominator == T(0)) {
|
||||
return 0;
|
||||
}
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
template<typename InputIterator>
|
||||
double median(InputIterator first, InputIterator last) {
|
||||
auto len = std::distance(first, last);
|
||||
auto iter = first + len / 2;
|
||||
|
||||
if (len % 2 == 0) {
|
||||
return (*iter + *(iter + 1)) / 2;
|
||||
} else {
|
||||
return *iter;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
double median(Container c) {
|
||||
return median(std::begin(c), std::end(c));
|
||||
}
|
||||
|
||||
|
||||
template<typename InputIterator>
|
||||
double geomean(InputIterator first, InputIterator last, double init=1.) {
|
||||
//Compute the geometric mean of the elments in range [first, last)
|
||||
//
|
||||
//To avoid potential round-off issues we transform the standard formula:
|
||||
//
|
||||
// geomean = ( v_1 * v_2 * ... * v_n) ^ (1/n)
|
||||
//
|
||||
//by taking the log:
|
||||
//
|
||||
// geomean = exp( (1 / n) * (log(v_1) + log(v_2) + ... + log(v_n)))
|
||||
|
||||
double log_sum = std::log(init);
|
||||
size_t n = 0;
|
||||
for(auto iter = first; iter != last; ++iter) {
|
||||
log_sum += std::log(*iter);
|
||||
n += 1;
|
||||
}
|
||||
|
||||
VTR_ASSERT(n > 0.);
|
||||
|
||||
return std::exp( (1. / n) * log_sum );
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
double geomean(Container c) {
|
||||
return geomean(std::begin(c), std::end(c));
|
||||
}
|
||||
|
||||
|
||||
template<typename InputIterator>
|
||||
double arithmean(InputIterator first, InputIterator last, double init=0.) {
|
||||
double sum = init;
|
||||
size_t n = 0;
|
||||
for (auto iter = first; iter != last; ++iter) {
|
||||
sum += *iter;
|
||||
n += 1;
|
||||
}
|
||||
|
||||
VTR_ASSERT(n > 0.);
|
||||
return sum / n;
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
double arithmean(Container c) {
|
||||
return arithmean(std::begin(c), std::end(c));
|
||||
}
|
||||
|
||||
|
||||
//Return the greatest common divisor of x and y
|
||||
// Note that T should be an integral type
|
||||
template<typename T>
|
||||
static T gcd(T x, T y){
|
||||
static_assert(std::is_integral<T>::value, "T must be integral");
|
||||
//Euclidean algorithm
|
||||
if (y == 0){
|
||||
return x;
|
||||
}
|
||||
return gcd(y, x % y);
|
||||
}
|
||||
|
||||
//Return the least common multiple of x and y
|
||||
// Note that T should be an integral type
|
||||
template<typename T>
|
||||
T lcm(T x, T y) {
|
||||
static_assert(std::is_integral<T>::value, "T must be integral");
|
||||
|
||||
if (x == 0 && y == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return (x / gcd(x,y)) * y;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr double DEFAULT_REL_TOL = 1e-9;
|
||||
constexpr double DEFAULT_ABS_TOL = 0;
|
||||
|
||||
template<class T>
|
||||
bool isclose(T a, T b, T rel_tol, T abs_tol) {
|
||||
|
||||
if (std::isinf(a) && std::isinf(b)) return (std::signbit(a) == std::signbit(b));
|
||||
if (std::isnan(a) && std::isnan(b)) return false;
|
||||
|
||||
T abs_largest = std::max(std::abs(a), std::abs(b));
|
||||
return std::abs(a - b) <= std::max(rel_tol * abs_largest, abs_tol);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool isclose(T a, T b) {
|
||||
return isclose<T>(a, b, DEFAULT_REL_TOL, DEFAULT_ABS_TOL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,177 @@
|
|||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_list.h"
|
||||
#include "vtr_memory.h"
|
||||
#include "vtr_error.h"
|
||||
#include "vtr_util.h"
|
||||
|
||||
#ifndef __GLIBC__
|
||||
#include <stdlib.h>
|
||||
#else
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace vtr {
|
||||
|
||||
|
||||
#ifndef __GLIBC__
|
||||
int malloc_trim(size_t /*pad*/) {
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int malloc_trim(size_t pad) {
|
||||
return ::malloc_trim(pad);
|
||||
}
|
||||
#endif
|
||||
|
||||
void* free(void *some){
|
||||
if(some){
|
||||
std::free(some);
|
||||
some = nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* calloc(size_t nelem, size_t size) {
|
||||
void *ret;
|
||||
if (nelem == 0) {
|
||||
return nullptr ;
|
||||
}
|
||||
|
||||
if ((ret = std::calloc(nelem, size)) == nullptr ) {
|
||||
throw VtrError("Unable to calloc memory.", __FILE__, __LINE__);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void* malloc(size_t size) {
|
||||
void *ret;
|
||||
if (size == 0) {
|
||||
return nullptr ;
|
||||
}
|
||||
|
||||
if ((ret = std::malloc(size)) == nullptr && size != 0) {
|
||||
throw VtrError("Unable to malloc memory.", __FILE__, __LINE__);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void* realloc(void *ptr, size_t size) {
|
||||
void *ret;
|
||||
|
||||
ret = std::realloc(ptr, size);
|
||||
if (nullptr == ret && size != 0) {
|
||||
throw VtrError(string_fmt("Unable to realloc memory (ptr=%p, size=%d).", ptr, size),
|
||||
__FILE__, __LINE__);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void* chunk_malloc(size_t size, t_chunk *chunk_info) {
|
||||
|
||||
/* This routine should be used for allocating fairly small data *
|
||||
* structures where memory-efficiency is crucial. This routine allocates *
|
||||
* large "chunks" of data, and parcels them out as requested. Whenever *
|
||||
* it mallocs a new chunk it adds it to the linked list pointed to by *
|
||||
* chunk_info->chunk_ptr_head. This list can be used to free the *
|
||||
* chunked memory. *
|
||||
* Information about the currently open "chunk" must be stored by the *
|
||||
* user program. chunk_info->mem_avail_ptr points to an int storing *
|
||||
* how many bytes are left in the current chunk, while *
|
||||
* chunk_info->next_mem_loc_ptr is the address of a pointer to the *
|
||||
* next free bytes in the chunk. To start a new chunk, simply set *
|
||||
* chunk_info->mem_avail_ptr = 0. Each independent set of data *
|
||||
* structures should use a new chunk. */
|
||||
|
||||
/* To make sure the memory passed back is properly aligned, I must *
|
||||
* only send back chunks in multiples of the worst-case alignment *
|
||||
* restriction of the machine. On most machines this should be *
|
||||
* a long, but on 64-bit machines it might be a long long or a *
|
||||
* double. Change the typedef below if this is the case. */
|
||||
|
||||
typedef size_t Align;
|
||||
|
||||
constexpr int CHUNK_SIZE = 32768;
|
||||
constexpr int FRAGMENT_THRESHOLD = 100;
|
||||
|
||||
char *tmp_ptr;
|
||||
int aligned_size;
|
||||
|
||||
VTR_ASSERT(chunk_info->mem_avail >= 0);
|
||||
|
||||
if ((size_t) (chunk_info->mem_avail) < size) { /* Need to malloc more memory. */
|
||||
if (size > CHUNK_SIZE) { /* Too big, use standard routine. */
|
||||
tmp_ptr = (char *) vtr::malloc(size);
|
||||
|
||||
/* When debugging, uncomment the code below to see if memory allocation size */
|
||||
/* makes sense */
|
||||
//#ifdef DEBUG
|
||||
// vtr_printf("NB: my_chunk_malloc got a request for %d bytes.\n", size);
|
||||
// vtr_printf("You should consider using vtr::malloc for such big requests.\n");
|
||||
// #endif
|
||||
|
||||
VTR_ASSERT(chunk_info != nullptr);
|
||||
chunk_info->chunk_ptr_head = insert_in_vptr_list(
|
||||
chunk_info->chunk_ptr_head, tmp_ptr);
|
||||
return (tmp_ptr);
|
||||
}
|
||||
|
||||
if (chunk_info->mem_avail < FRAGMENT_THRESHOLD) { /* Only a small scrap left. */
|
||||
chunk_info->next_mem_loc_ptr = (char *) vtr::malloc(CHUNK_SIZE);
|
||||
chunk_info->mem_avail = CHUNK_SIZE;
|
||||
VTR_ASSERT(chunk_info != nullptr);
|
||||
chunk_info->chunk_ptr_head = insert_in_vptr_list(
|
||||
chunk_info->chunk_ptr_head, chunk_info->next_mem_loc_ptr);
|
||||
}
|
||||
|
||||
/* Execute else clause only when the chunk we want is pretty big, *
|
||||
* and would leave too big an unused fragment. Then we use malloc *
|
||||
* to allocate normally. */
|
||||
|
||||
else {
|
||||
tmp_ptr = (char *) vtr::malloc(size);
|
||||
VTR_ASSERT(chunk_info != nullptr);
|
||||
chunk_info->chunk_ptr_head = insert_in_vptr_list(
|
||||
chunk_info->chunk_ptr_head, tmp_ptr);
|
||||
return (tmp_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the smallest distance to advance the memory pointer and keep *
|
||||
* everything aligned. */
|
||||
|
||||
if (size % sizeof(Align) == 0) {
|
||||
aligned_size = size;
|
||||
} else {
|
||||
aligned_size = size + sizeof(Align) - size % sizeof(Align);
|
||||
}
|
||||
|
||||
tmp_ptr = chunk_info->next_mem_loc_ptr;
|
||||
chunk_info->next_mem_loc_ptr += aligned_size;
|
||||
chunk_info->mem_avail -= aligned_size;
|
||||
return (tmp_ptr);
|
||||
}
|
||||
|
||||
void free_chunk_memory(t_chunk *chunk_info) {
|
||||
|
||||
/* Frees the memory allocated by a sequence of calls to my_chunk_malloc. */
|
||||
|
||||
t_linked_vptr *curr_ptr, *prev_ptr;
|
||||
|
||||
curr_ptr = chunk_info->chunk_ptr_head;
|
||||
|
||||
while (curr_ptr != nullptr ) {
|
||||
free(curr_ptr->data_vptr); /* Free memory "chunk". */
|
||||
prev_ptr = curr_ptr;
|
||||
curr_ptr = curr_ptr->next;
|
||||
free(prev_ptr); /* Free memory used to track "chunk". */
|
||||
}
|
||||
chunk_info->chunk_ptr_head = nullptr;
|
||||
chunk_info->mem_avail = 0;
|
||||
chunk_info->next_mem_loc_ptr = nullptr;
|
||||
}
|
||||
|
||||
} //namespace
|
|
@ -0,0 +1,51 @@
|
|||
#ifndef VTR_MEMORY_H
|
||||
#define VTR_MEMORY_H
|
||||
#include <cstddef>
|
||||
|
||||
namespace vtr {
|
||||
struct t_linked_vptr; //Forward declaration
|
||||
|
||||
/* This structure is to keep track of chunks of memory that is being *
|
||||
* allocated to save overhead when allocating very small memory pieces. *
|
||||
* For a complete description, please see the comment in chunk_malloc*/
|
||||
struct t_chunk {
|
||||
t_linked_vptr *chunk_ptr_head = nullptr;
|
||||
/* chunk_ptr_head->data_vptr: head of the entire linked
|
||||
* list of allocated "chunk" memory;
|
||||
* chunk_ptr_head->next: pointer to the next chunk on the linked list*/
|
||||
int mem_avail = 0; /* number of bytes left in the current chunk */
|
||||
char *next_mem_loc_ptr = nullptr;/* pointer to the first available (free) *
|
||||
* byte in the current chunk */
|
||||
};
|
||||
|
||||
void* free(void *some);
|
||||
void* calloc(size_t nelem, size_t size);
|
||||
void* malloc(size_t size);
|
||||
void* realloc(void *ptr, size_t size);
|
||||
|
||||
void* chunk_malloc(size_t size, t_chunk *chunk_info);
|
||||
void free_chunk_memory(t_chunk *chunk_info);
|
||||
|
||||
//Like chunk_malloc, but with proper C++ object initialization
|
||||
template<typename T>
|
||||
T* chunk_new(t_chunk *chunk_info) {
|
||||
void* block = chunk_malloc(sizeof(T), chunk_info);
|
||||
|
||||
return new (block) T(); //Placement new
|
||||
}
|
||||
|
||||
//Call the destructor of an obj which must have been allocated in the specified chunk
|
||||
template<typename T>
|
||||
void chunk_delete(T* obj, t_chunk * /*chunk_info*/) {
|
||||
if (obj) {
|
||||
obj->~T(); //Manually call destructor
|
||||
//Currently we don't mark the unused memory as free
|
||||
}
|
||||
}
|
||||
|
||||
//Cross platform wrapper around GNU's malloc_trim()
|
||||
// TODO: This is only used in one place within VPR, consider removing it
|
||||
int malloc_trim(size_t pad);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,359 @@
|
|||
#ifndef VTR_ND_MATRIX_H
|
||||
#define VTR_ND_MATRIX_H
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
|
||||
namespace vtr {
|
||||
|
||||
//Proxy class for a sub-matrix of a NdMatrix class.
|
||||
//This is used to allow chaining of array indexing [] operators in a natural way.
|
||||
//
|
||||
//Each instance of this class peels off one-dimension and returns a NdMatrixProxy representing
|
||||
//the resulting sub-matrix. This is repeated recursively until we hit the 1-dimensional base-case.
|
||||
//
|
||||
//Since this expansion happens at compiler time all the proxy classes get optimized away,
|
||||
//yielding both high performance and generality.
|
||||
//
|
||||
//Recursive case: N-dimensional array
|
||||
template<typename T, size_t N>
|
||||
class NdMatrixProxy {
|
||||
public:
|
||||
static_assert(N > 0, "Must have at least one dimension");
|
||||
|
||||
//Construct a matrix proxy object
|
||||
//
|
||||
// dim_sizes: Array of dimension sizes
|
||||
// idim: The dimension associated with this proxy
|
||||
// dim_stride: The stride of this dimension (i.e. how many element in memory between indicies of this dimension)
|
||||
// start: Pointer to the start of the sub-matrix this proxy represents
|
||||
NdMatrixProxy<T,N>(const size_t* dim_sizes, size_t idim, size_t dim_stride, T* start)
|
||||
: dim_sizes_(dim_sizes)
|
||||
, idim_(idim)
|
||||
, dim_stride_(dim_stride)
|
||||
, start_(start) {}
|
||||
|
||||
const NdMatrixProxy<T,N-1> operator[](size_t index) const {
|
||||
VTR_ASSERT_SAFE_MSG(index >= 0, "Index out of range (below dimension minimum)");
|
||||
VTR_ASSERT_SAFE_MSG(index < dim_sizes_[idim_], "Index out of range (above dimension maximum)");
|
||||
|
||||
size_t next_dim_size = dim_sizes_[idim_ + 1];
|
||||
VTR_ASSERT_SAFE_MSG(next_dim_size > 0, "Can not index into zero-sized dimension");
|
||||
|
||||
//Determine the stride of the next dimension
|
||||
size_t next_dim_stride = dim_stride_ / next_dim_size;
|
||||
|
||||
//Strip off one dimension
|
||||
return NdMatrixProxy<T,N-1>(dim_sizes_, //Pass the dimension information
|
||||
idim_ + 1, //Pass the next dimension
|
||||
next_dim_stride, //Pass the stride for the next dimension
|
||||
start_ + dim_stride_*index); //Advance to index in this dimension
|
||||
}
|
||||
|
||||
NdMatrixProxy<T,N-1> operator[](size_t index) {
|
||||
//Call the const version and cast-away constness
|
||||
return const_cast<const NdMatrixProxy<T,N>*>(this)->operator[](index);
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t* dim_sizes_;
|
||||
const size_t idim_;
|
||||
const size_t dim_stride_;
|
||||
T* start_;
|
||||
};
|
||||
|
||||
//Base case: 1-dimensional array
|
||||
template<typename T>
|
||||
class NdMatrixProxy<T,1> {
|
||||
public:
|
||||
NdMatrixProxy<T,1>(const size_t* dim_sizes, size_t idim, size_t dim_stride, T* start)
|
||||
: dim_sizes_(dim_sizes)
|
||||
, idim_(idim)
|
||||
, dim_stride_(dim_stride)
|
||||
, start_(start) {}
|
||||
|
||||
|
||||
const T& operator[](size_t index) const {
|
||||
VTR_ASSERT_SAFE_MSG(dim_stride_ == 1, "Final dimension must have stride 1");
|
||||
VTR_ASSERT_SAFE_MSG(index >= 0, "Index out of range (below dimension minimum)");
|
||||
VTR_ASSERT_SAFE_MSG(index < dim_sizes_[idim_], "Index out of range (above dimension maximum)");
|
||||
|
||||
//Base case
|
||||
return start_[index];
|
||||
}
|
||||
|
||||
T& operator[](size_t index) {
|
||||
//Call the const version and cast-away constness
|
||||
return const_cast<T&>(const_cast<const NdMatrixProxy<T,1>*>(this)->operator[](index));
|
||||
}
|
||||
|
||||
//For legacy compatibility (i.e. code expecting a pointer) we allow this base dimension
|
||||
//case to retrieve a raw pointer to the last dimension elements.
|
||||
//
|
||||
//Note that it is the caller's responsibility to use this correctly; care must be taken
|
||||
//not to clobber elements in other dimensions
|
||||
const T* data() const {
|
||||
return start_;
|
||||
}
|
||||
|
||||
T* data() {
|
||||
//Call the const version and cast-away constness
|
||||
return const_cast<T*>(const_cast<const NdMatrixProxy<T,1>*>(this)->data());
|
||||
}
|
||||
|
||||
private:
|
||||
const size_t* dim_sizes_;
|
||||
const size_t idim_;
|
||||
const size_t dim_stride_;
|
||||
T* start_;
|
||||
};
|
||||
|
||||
//Base class for an N-dimensional matrix supporting arbitrary index ranges per dimension.
|
||||
//This class implements all of the matrix handling (lifetime etc.) except for indexing
|
||||
//(which is implemented in the NdMatrix class). Indexing is split out to allows specialization
|
||||
//of indexing for N = 1.
|
||||
//
|
||||
//Implementation:
|
||||
//
|
||||
//This class uses a single linear array to store the matrix in c-style (row major)
|
||||
//order. That is, the right-most index is laid out contiguous memory.
|
||||
//
|
||||
//This should improve memory usage (no extra pointers to store for each dimension),
|
||||
//and cache locality (less indirection via pointers, predictable strides).
|
||||
//
|
||||
//The indicies are calculated based on the dimensions to access the appropriate elements.
|
||||
//Since the indexing calculations are visible to the compiler at compile time they can be
|
||||
//optimized to be efficient.
|
||||
template<typename T, size_t N>
|
||||
class NdMatrixBase {
|
||||
public:
|
||||
static_assert(N >= 1, "Minimum dimension 1");
|
||||
|
||||
//An empty matrix (all dimensions size zero)
|
||||
NdMatrixBase() {
|
||||
clear();
|
||||
}
|
||||
|
||||
//Specified dimension sizes:
|
||||
// [0..dim_sizes[0])
|
||||
// [0..dim_sizes[1])
|
||||
// ...
|
||||
//with optional fill value
|
||||
NdMatrixBase(std::array<size_t,N> dim_sizes, T value=T()) {
|
||||
resize(dim_sizes, value);
|
||||
}
|
||||
|
||||
public: //Accessors
|
||||
//Returns the size of the matrix (number of elements)
|
||||
size_t size() const {
|
||||
VTR_ASSERT_DEBUG_MSG(calc_size() == size_, "Calculated and current matrix size must be consistent");
|
||||
return size_;
|
||||
}
|
||||
|
||||
//Returns true if there are no elements in the matrix
|
||||
bool empty() const {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
//Returns the number of dimensions (i.e. N)
|
||||
size_t ndims() const {
|
||||
return dim_sizes_.size();
|
||||
}
|
||||
|
||||
//Returns the size of the ith dimension
|
||||
size_t dim_size(size_t i) const {
|
||||
VTR_ASSERT_SAFE(i < ndims());
|
||||
|
||||
return dim_sizes_[i];
|
||||
}
|
||||
|
||||
//Returns the starting index of ith dimension
|
||||
size_t begin_index(size_t i) const {
|
||||
VTR_ASSERT_SAFE(i < ndims());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Returns the one-past-the-end index of the ith dimension
|
||||
size_t end_index(size_t i) const {
|
||||
VTR_ASSERT_SAFE(i < ndims());
|
||||
|
||||
return dim_sizes_[i];
|
||||
}
|
||||
|
||||
public: //Mutators
|
||||
|
||||
//Set all elements to 'value'
|
||||
void fill(T value) {
|
||||
std::fill(data_.get(), data_.get() + size(), value);
|
||||
}
|
||||
|
||||
//Resize the matrix to the specified dimension ranges
|
||||
//
|
||||
//If 'value' is specified all elements will be initialized to it,
|
||||
//otherwise they will be default constructed.
|
||||
void resize(std::array<size_t,N> dim_sizes, T value=T()) {
|
||||
dim_sizes_ = dim_sizes;
|
||||
size_ = calc_size();
|
||||
alloc();
|
||||
fill(value);
|
||||
}
|
||||
|
||||
//Reset the matrix to size zero
|
||||
void clear() {
|
||||
data_.reset(nullptr);
|
||||
dim_sizes_.fill(0);
|
||||
size_ = 0;
|
||||
}
|
||||
public: //Lifetime management
|
||||
//Copy constructor
|
||||
NdMatrixBase(const NdMatrixBase& other)
|
||||
: NdMatrixBase(other.dim_sizes_) {
|
||||
std::copy(other.data_.get(), other.data_.get() + other.size(), data_.get());
|
||||
}
|
||||
|
||||
//Move constructor
|
||||
NdMatrixBase(NdMatrixBase&& other)
|
||||
: NdMatrixBase() {
|
||||
swap(*this, other);
|
||||
}
|
||||
|
||||
//Copy/move assignment
|
||||
//
|
||||
//Note that rhs is taken by value (copy-swap idiom)
|
||||
NdMatrixBase& operator=(NdMatrixBase rhs) {
|
||||
swap(*this, rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//Swap two NdMatrixBase objects
|
||||
friend void swap(NdMatrixBase<T,N>& m1, NdMatrixBase<T,N>& m2) {
|
||||
using std::swap;
|
||||
swap(m1.size_, m2.size_);
|
||||
swap(m1.dim_sizes_, m2.dim_sizes_);
|
||||
swap(m1.data_, m2.data_);
|
||||
}
|
||||
|
||||
private:
|
||||
//Allocate space for all the elements
|
||||
void alloc() {
|
||||
data_ = std::make_unique<T[]>(size());
|
||||
}
|
||||
|
||||
//Returns the size of the matrix (number of elements) calucated
|
||||
//from the current dimensions
|
||||
size_t calc_size() const {
|
||||
//Size is the product of all dimension sizes
|
||||
size_t cnt = dim_size(0);
|
||||
for (size_t idim = 1; idim < ndims(); ++idim) {
|
||||
cnt *= dim_size(idim);
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
protected:
|
||||
size_t size_ = 0;
|
||||
std::array<size_t,N> dim_sizes_;
|
||||
std::unique_ptr<T[]> data_ = nullptr;
|
||||
};
|
||||
|
||||
//An N-dimensional matrix supporting arbitrary (continuous) index ranges
|
||||
//per dimension.
|
||||
//
|
||||
//Examples:
|
||||
//
|
||||
// //A 2-dimensional matrix with indicies [0..4][0..9]
|
||||
// NdMatrix<int,2> m1({5,10});
|
||||
//
|
||||
// //Accessing an element
|
||||
// int i = m1[3][5];
|
||||
//
|
||||
// //Setting an element
|
||||
// m1[2][8] = 0;
|
||||
//
|
||||
// //A 3-dimensional matrix with indicies [0..4][0..9][0..19]
|
||||
// NdMatrix<int,3> m2({5,10,20});
|
||||
//
|
||||
// //A 2-dimensional matrix with indicies [0..4][0..9], with all entries
|
||||
// //initialized to 42
|
||||
// NdMatrix<int,2> m3({5,10}, 42);
|
||||
//
|
||||
// //Filling all entries with value 101
|
||||
// m3.fill(101);
|
||||
//
|
||||
// //Resizing an existing matrix (all values reset to default constucted value)
|
||||
// m3.resize({5,5})
|
||||
//
|
||||
// //Resizing an existing matrix (all elements set to value 88)
|
||||
// m3.resize({15,55}, 88)
|
||||
template<typename T, size_t N>
|
||||
class NdMatrix : public NdMatrixBase<T,N> {
|
||||
//General case
|
||||
static_assert(N >= 2, "Minimum dimension 2");
|
||||
|
||||
public:
|
||||
//Use the base constructors
|
||||
using NdMatrixBase<T,N>::NdMatrixBase;
|
||||
public:
|
||||
//Access an element
|
||||
//
|
||||
//Returns a proxy-object to allow chained array-style indexing (N >= 2 case)
|
||||
const NdMatrixProxy<T,N-1> operator[](size_t index) const {
|
||||
VTR_ASSERT_SAFE_MSG(this->dim_size(0) > 0, "Can not index into size zero dimension");
|
||||
VTR_ASSERT_SAFE_MSG(this->dim_size(1) > 0, "Can not index into size zero dimension");
|
||||
VTR_ASSERT_SAFE_MSG(index >= 0, "Index out of range (below dimension minimum)");
|
||||
VTR_ASSERT_SAFE_MSG(index < this->dim_sizes_[0], "Index out of range (above dimension maximum)");
|
||||
|
||||
//Calculate the stride for the current dimension
|
||||
size_t dim_stride = this->size() / this->dim_size(0);
|
||||
|
||||
//Calculate the stride for the next dimension
|
||||
size_t next_dim_stride = dim_stride / this->dim_size(1);
|
||||
|
||||
//Peel off the first dimension
|
||||
return NdMatrixProxy<T,N-1>(this->dim_sizes_.data(), //Pass the dimension information
|
||||
1, //Pass the next dimension
|
||||
next_dim_stride, //Pass the stride for the next dimension
|
||||
this->data_.get() + dim_stride*index); //Advance to index in this dimension
|
||||
}
|
||||
|
||||
//Access an element
|
||||
//
|
||||
//Returns a proxy-object to allow chained array-style indexing
|
||||
NdMatrixProxy<T,N-1> operator[](size_t index) {
|
||||
//Call the const version, since returned by value don't need to worry about const
|
||||
return const_cast<const NdMatrix<T,N>*>(this)->operator[](index);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class NdMatrix<T,1> : public NdMatrixBase<T,1> {
|
||||
//Specialization for N = 1
|
||||
public:
|
||||
//Use the base constructors
|
||||
using NdMatrixBase<T,1>::NdMatrixBase;
|
||||
public:
|
||||
//Access an element (immutable)
|
||||
const T& operator[](size_t index) const {
|
||||
VTR_ASSERT_SAFE_MSG(this->dim_size(0) > 0, "Can not index into size zero dimension");
|
||||
VTR_ASSERT_SAFE_MSG(index >= 0, "Index out of range (below dimension minimum)");
|
||||
VTR_ASSERT_SAFE_MSG(index < this->dim_sizes_[0], "Index out of range (above dimension maximum)");
|
||||
|
||||
return this->data_[index];
|
||||
}
|
||||
|
||||
//Access an element (mutable)
|
||||
T& operator[](size_t index) {
|
||||
//Call the const version, and cast away const-ness
|
||||
return const_cast<T&>(const_cast<const NdMatrix<T,1>*>(this)->operator[](index));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
//Convenient short forms for common NdMatricies
|
||||
template<typename T>
|
||||
using Matrix = NdMatrix<T,2>;
|
||||
|
||||
} //namespace
|
||||
#endif
|
|
@ -0,0 +1,400 @@
|
|||
#ifndef VTR_ND_OFFSET_MATRIX_H
|
||||
#define VTR_ND_OFFSET_MATRIX_H
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
|
||||
namespace vtr {
|
||||
|
||||
//A half-open range specification for a matrix dimension [begin_index, last_index),
|
||||
//with valid indicies from [begin_index() ... end_index()-1], provided size() > 0.
|
||||
class DimRange {
|
||||
public:
|
||||
DimRange() = default;
|
||||
DimRange(size_t begin, size_t end)
|
||||
: begin_index_(begin)
|
||||
, end_index_(end) {}
|
||||
|
||||
size_t begin_index() const { return begin_index_; }
|
||||
size_t end_index() const { return end_index_; }
|
||||
|
||||
size_t size() const { return end_index_ - begin_index_; }
|
||||
private:
|
||||
size_t begin_index_ = 0;
|
||||
size_t end_index_ = 0;
|
||||
};
|
||||
|
||||
//Proxy class for a sub-matrix of a NdOffsetMatrix class.
|
||||
//This is used to allow chaining of array indexing [] operators in a natural way.
|
||||
//
|
||||
//Each instance of this class peels off one-dimension and returns a NdOffsetMatrixProxy representing
|
||||
//the resulting sub-matrix. This is repeated recursively until we hit the 1-dimensional base-case.
|
||||
//
|
||||
//Since this expansion happens at compiler time all the proxy classes get optimized away,
|
||||
//yielding both high performance and generality.
|
||||
//
|
||||
//Recursive case: N-dimensional array
|
||||
template<typename T, size_t N>
|
||||
class NdOffsetMatrixProxy {
|
||||
public:
|
||||
static_assert(N > 0, "Must have at least one dimension");
|
||||
|
||||
//Construct a matrix proxy object
|
||||
//
|
||||
// dim_ranges: Array of DimRange objects
|
||||
// idim: The dimension associated with this proxy
|
||||
// dim_stride: The stride of this dimension (i.e. how many element in memory between indicies of this dimension)
|
||||
// start: Pointer to the start of the sub-matrix this proxy represents
|
||||
NdOffsetMatrixProxy<T,N>(const DimRange* dim_ranges, size_t idim, size_t dim_stride, T* start)
|
||||
: dim_ranges_(dim_ranges)
|
||||
, idim_(idim)
|
||||
, dim_stride_(dim_stride)
|
||||
, start_(start) {}
|
||||
|
||||
const NdOffsetMatrixProxy<T,N-1> operator[](size_t index) const {
|
||||
VTR_ASSERT_SAFE_MSG(index >= dim_ranges_[idim_].begin_index(), "Index out of range (below dimension minimum)");
|
||||
VTR_ASSERT_SAFE_MSG(index < dim_ranges_[idim_].end_index(), "Index out of range (above dimension maximum)");
|
||||
|
||||
//The elements are stored in zero-indexed form, so we need to adjust
|
||||
//for any non-zero minimum index
|
||||
size_t effective_index = index - dim_ranges_[idim_].begin_index();
|
||||
|
||||
//Determine the stride of the next dimension
|
||||
size_t next_dim_stride = dim_stride_ / dim_ranges_[idim_ + 1].size();
|
||||
|
||||
//Strip off one dimension
|
||||
return NdOffsetMatrixProxy<T,N-1>(dim_ranges_, //Pass the dimension information
|
||||
idim_ + 1, //Pass the next dimension
|
||||
next_dim_stride, //Pass the stride for the next dimension
|
||||
start_ + dim_stride_*effective_index); //Advance to index in this dimension
|
||||
}
|
||||
|
||||
NdOffsetMatrixProxy<T,N-1> operator[](size_t index) {
|
||||
//Call the const version and cast-away constness
|
||||
return const_cast<const NdOffsetMatrixProxy<T,N>*>(this)->operator[](index);
|
||||
}
|
||||
|
||||
private:
|
||||
const DimRange* dim_ranges_;
|
||||
const size_t idim_;
|
||||
const size_t dim_stride_;
|
||||
T* start_;
|
||||
};
|
||||
|
||||
//Base case: 1-dimensional array
|
||||
template<typename T>
|
||||
class NdOffsetMatrixProxy<T,1> {
|
||||
public:
|
||||
NdOffsetMatrixProxy<T,1>(const DimRange* dim_ranges, size_t idim, size_t dim_stride, T* start)
|
||||
: dim_ranges_(dim_ranges)
|
||||
, idim_(idim)
|
||||
, dim_stride_(dim_stride)
|
||||
, start_(start) {}
|
||||
|
||||
|
||||
const T& operator[](size_t index) const {
|
||||
VTR_ASSERT_SAFE_MSG(dim_stride_ == 1, "Final dimension must have stride 1");
|
||||
VTR_ASSERT_SAFE_MSG(index >= dim_ranges_[idim_].begin_index(), "Index out of range (below dimension minimum)");
|
||||
VTR_ASSERT_SAFE_MSG(index < dim_ranges_[idim_].end_index(), "Index out of range (above dimension maximum)");
|
||||
|
||||
//The elements are stored in zero-indexed form, so we need to adjust
|
||||
//for any non-zero minimum index
|
||||
size_t effective_index = index - dim_ranges_[idim_].begin_index();
|
||||
|
||||
//Base case
|
||||
return start_[effective_index];
|
||||
}
|
||||
|
||||
T& operator[](size_t index) {
|
||||
//Call the const version and cast-away constness
|
||||
return const_cast<T&>(const_cast<const NdOffsetMatrixProxy<T,1>*>(this)->operator[](index));
|
||||
}
|
||||
|
||||
private:
|
||||
const DimRange* dim_ranges_;
|
||||
const size_t idim_;
|
||||
const size_t dim_stride_;
|
||||
T* start_;
|
||||
};
|
||||
|
||||
//Base class for an N-dimensional matrix supporting arbitrary index ranges per dimension.
|
||||
//This class implements all of the matrix handling (lifetime etc.) except for indexing
|
||||
//(which is implemented in the NdOffsetMatrix class). Indexing is split out to allows specialization
|
||||
//of indexing for N = 1.
|
||||
//
|
||||
//Implementation:
|
||||
//
|
||||
//This class uses a single linear array to store the matrix in c-style (row major)
|
||||
//order. That is, the right-most index is laid out contiguous memory.
|
||||
//
|
||||
//This should improve memory usage (no extra pointers to store for each dimension),
|
||||
//and cache locality (less indirection via pointers, predictable strides).
|
||||
//
|
||||
//The indicies are calculated based on the dimensions to access the appropriate elements.
|
||||
//Since the indexing calculations are visible to the compiler at compile time they can be
|
||||
//optimized to be efficient.
|
||||
template<typename T, size_t N>
|
||||
class NdOffsetMatrixBase {
|
||||
public:
|
||||
static_assert(N >= 1, "Minimum dimension 1");
|
||||
|
||||
//An empty matrix (all dimensions size zero)
|
||||
NdOffsetMatrixBase() {
|
||||
clear();
|
||||
}
|
||||
|
||||
//Specified dimension sizes:
|
||||
// [0..dim_sizes[0])
|
||||
// [0..dim_sizes[1])
|
||||
// ...
|
||||
//with optional fill value
|
||||
NdOffsetMatrixBase(std::array<size_t,N> dim_sizes, T value=T()) {
|
||||
resize(dim_sizes, value);
|
||||
}
|
||||
|
||||
//Specified dimension index ranges:
|
||||
// [dim_ranges[0].begin_index() ... dim_ranges[1].end_index())
|
||||
// [dim_ranges[1].begin_index() ... dim_ranges[1].end_index())
|
||||
// ...
|
||||
//with optional fill value
|
||||
NdOffsetMatrixBase(std::array<DimRange,N> dim_ranges, T value=T()) {
|
||||
resize(dim_ranges, value);
|
||||
}
|
||||
public: //Accessors
|
||||
//Returns the size of the matrix (number of elements)
|
||||
size_t size() const {
|
||||
//Size is the product of all dimension sizes
|
||||
size_t cnt = dim_size(0);
|
||||
for (size_t idim = 1; idim < ndims(); ++idim) {
|
||||
cnt *= dim_size(idim);
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
//Returns true if there are no elements in the matrix
|
||||
bool empty() const {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
//Returns the number of dimensions (i.e. N)
|
||||
size_t ndims() const {
|
||||
return dim_ranges_.size();
|
||||
}
|
||||
|
||||
//Returns the size of the ith dimension
|
||||
size_t dim_size(size_t i) const {
|
||||
VTR_ASSERT_SAFE(i < ndims());
|
||||
|
||||
return dim_ranges_[i].size();
|
||||
}
|
||||
|
||||
//Returns the starting index of ith dimension
|
||||
size_t begin_index(size_t i) const {
|
||||
VTR_ASSERT_SAFE(i < ndims());
|
||||
|
||||
return dim_ranges_[i].begin_index();
|
||||
}
|
||||
|
||||
//Returns the one-past-the-end index of the ith dimension
|
||||
size_t end_index(size_t i) const {
|
||||
VTR_ASSERT_SAFE(i < ndims());
|
||||
|
||||
return dim_ranges_[i].end_index();
|
||||
}
|
||||
|
||||
public: //Mutators
|
||||
|
||||
//Set all elements to 'value'
|
||||
void fill(T value) {
|
||||
std::fill(data_.get(), data_.get() + size(), value);
|
||||
}
|
||||
|
||||
//Resize the matrix to the specified dimensions
|
||||
//
|
||||
//If 'value' is specified all elements will be initialized to it,
|
||||
//otherwise they will be default constructed.
|
||||
void resize(std::array<size_t,N> dim_sizes, T value=T()) {
|
||||
//Convert dimension to range [0..dim)
|
||||
for(size_t i = 0; i < dim_sizes.size(); ++i) {
|
||||
dim_ranges_[i] = {0, dim_sizes[i]};
|
||||
}
|
||||
alloc();
|
||||
fill(value);
|
||||
}
|
||||
|
||||
//Resize the matrix to the specified dimension ranges
|
||||
//
|
||||
//If 'value' is specified all elements will be initialized to it,
|
||||
//otherwise they will be default constructed.
|
||||
void resize(std::array<DimRange,N> dim_ranges, T value=T()) {
|
||||
dim_ranges_ = dim_ranges;
|
||||
alloc();
|
||||
fill(value);
|
||||
}
|
||||
|
||||
//Reset the matrix to size zero
|
||||
void clear() {
|
||||
data_.reset(nullptr);
|
||||
for(size_t i = 0; i < dim_ranges_.size(); ++i) {
|
||||
dim_ranges_[i] = {0, 0};
|
||||
}
|
||||
}
|
||||
public: //Lifetime management
|
||||
//Copy constructor
|
||||
NdOffsetMatrixBase(const NdOffsetMatrixBase& other)
|
||||
: NdOffsetMatrixBase(other.dim_ranges_) {
|
||||
std::copy(other.data_.get(), other.data_.get() + other.size(), data_.get());
|
||||
}
|
||||
|
||||
//Move constructor
|
||||
NdOffsetMatrixBase(NdOffsetMatrixBase&& other)
|
||||
: NdOffsetMatrixBase() {
|
||||
swap(*this, other);
|
||||
}
|
||||
|
||||
//Copy/move assignment
|
||||
//
|
||||
//Note that rhs is taken by value (copy-swap idiom)
|
||||
NdOffsetMatrixBase& operator=(NdOffsetMatrixBase rhs) {
|
||||
swap(*this, rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//Swap two NdOffsetMatrixBase objects
|
||||
friend void swap(NdOffsetMatrixBase<T,N>& m1, NdOffsetMatrixBase<T,N>& m2) {
|
||||
using std::swap;
|
||||
swap(m1.dim_ranges_, m2.dim_ranges_);
|
||||
swap(m1.data_, m2.data_);
|
||||
}
|
||||
|
||||
private:
|
||||
//Allocate space for all the elements
|
||||
void alloc() {
|
||||
data_ = std::make_unique<T[]>(size());
|
||||
}
|
||||
|
||||
protected:
|
||||
std::array<DimRange,N> dim_ranges_;
|
||||
std::unique_ptr<T[]> data_ = nullptr;
|
||||
};
|
||||
|
||||
//An N-dimensional matrix supporting arbitrary (continuous) index ranges
|
||||
//per dimension.
|
||||
//
|
||||
//If no second template parameter is provided defaults to a 2-dimensional
|
||||
//matrix
|
||||
//
|
||||
//Examples:
|
||||
//
|
||||
// //A 2-dimensional matrix with indicies [0..4][0..9]
|
||||
// NdOffsetMatrix<int,2> m1({5,10});
|
||||
//
|
||||
// //Accessing an element
|
||||
// int i = m4[3][5];
|
||||
//
|
||||
// //Setting an element
|
||||
// m4[6][20] = 0;
|
||||
//
|
||||
// //A 2-dimensional matrix with indicies [2..6][5..9]
|
||||
// // Note that C++ requires one more set of curly brace than you would expect
|
||||
// NdOffsetMatrix<int,2> m2({{{2,7},{5,10}}});
|
||||
//
|
||||
// //A 3-dimensional matrix with indicies [0..4][0..9][0..19]
|
||||
// NdOffsetMatrix<int,3> m3({5,10,20});
|
||||
//
|
||||
// //A 3-dimensional matrix with indicies [2..6][1..19][50..89]
|
||||
// NdOffsetMatrix<int,3> m4({{{2,7}, {1,20}, {50,90}}});
|
||||
//
|
||||
// //A 2-dimensional matrix with indicies [2..6][1..20], with all entries
|
||||
// //intialized to 42
|
||||
// NdOffsetMatrix<int,2> m4({{{2,7}, {1,21}}}, 42);
|
||||
//
|
||||
// //A 2-dimensional matrix with indicies [0..4][0..9], with all entries
|
||||
// //initialized to 42
|
||||
// NdOffsetMatrix<int,2> m1({5,10}, 42);
|
||||
//
|
||||
// //Filling all entries with value 101
|
||||
// m1.fill(101);
|
||||
//
|
||||
// //Resizing an existing matrix (all values reset to default constucted value)
|
||||
// m1.resize({5,5})
|
||||
//
|
||||
// //Resizing an existing matrix (all elements set to value 88)
|
||||
// m1.resize({15,55}, 88)
|
||||
template<typename T, size_t N>
|
||||
class NdOffsetMatrix : public NdOffsetMatrixBase<T,N> {
|
||||
//General case
|
||||
static_assert(N >= 2, "Minimum dimension 2");
|
||||
|
||||
public:
|
||||
//Use the base constructors
|
||||
using NdOffsetMatrixBase<T,N>::NdOffsetMatrixBase;
|
||||
public:
|
||||
//Access an element
|
||||
//
|
||||
//Returns a proxy-object to allow chained array-style indexing (N >= 2 case)
|
||||
//template<typename = typename std::enable_if<N >= 2>::type, typename T1=T>
|
||||
const NdOffsetMatrixProxy<T,N-1> operator[](size_t index) const {
|
||||
VTR_ASSERT_SAFE_MSG(this->dim_size(0) > 0, "Can not index into size zero dimension");
|
||||
VTR_ASSERT_SAFE_MSG(this->dim_size(1) > 0, "Can not index into size zero dimension");
|
||||
VTR_ASSERT_SAFE_MSG(index >= this->dim_ranges_[0].begin_index(), "Index out of range (below dimension minimum)");
|
||||
VTR_ASSERT_SAFE_MSG(index < this->dim_ranges_[0].end_index(), "Index out of range (above dimension maximum)");
|
||||
|
||||
//The elements are stored in zero-indexed form, so adjust for any
|
||||
//non-zero minimum index in this dimension
|
||||
size_t effective_index = index - this->dim_ranges_[0].begin_index();
|
||||
|
||||
//Calculate the stride for the current dimension
|
||||
size_t dim_stride = this->size() / this->dim_size(0);
|
||||
|
||||
//Calculate the stride for the next dimension
|
||||
size_t next_dim_stride = dim_stride / this->dim_size(1);
|
||||
|
||||
//Peel off the first dimension
|
||||
return NdOffsetMatrixProxy<T,N-1>(this->dim_ranges_.data(), //Pass the dimension information
|
||||
1, //Pass the next dimension
|
||||
next_dim_stride, //Pass the stride for the next dimension
|
||||
this->data_.get() + dim_stride*effective_index); //Advance to index in this dimension
|
||||
}
|
||||
|
||||
//Access an element
|
||||
//
|
||||
//Returns a proxy-object to allow chained array-style indexing
|
||||
NdOffsetMatrixProxy<T,N-1> operator[](size_t index) {
|
||||
//Call the const version, since returned by value don't need to worry about const
|
||||
return const_cast<const NdOffsetMatrix<T,N>*>(this)->operator[](index);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class NdOffsetMatrix<T,1> : public NdOffsetMatrixBase<T,1> {
|
||||
//Specialization for N = 1
|
||||
public:
|
||||
//Use the base constructors
|
||||
using NdOffsetMatrixBase<T,1>::NdOffsetMatrixBase;
|
||||
public:
|
||||
//Access an element (immutable)
|
||||
const T& operator[](size_t index) const {
|
||||
VTR_ASSERT_SAFE_MSG(this->dim_size(0) > 0, "Can not index into size zero dimension");
|
||||
VTR_ASSERT_SAFE_MSG(index >= this->dim_ranges_[0].begin_index(), "Index out of range (below dimension minimum)");
|
||||
VTR_ASSERT_SAFE_MSG(index < this->dim_ranges_[0].end_index(), "Index out of range (above dimension maximum)");
|
||||
|
||||
return this->data_[index];
|
||||
}
|
||||
|
||||
//Access an element (mutable)
|
||||
T& operator[](size_t index) {
|
||||
//Call the const version, and cast away const-ness
|
||||
return const_cast<T&>(const_cast<const NdOffsetMatrix<T,1>*>(this)->operator[](index));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
//Convenient short forms for common NdMatricies
|
||||
template<typename T>
|
||||
using OffsetMatrix = NdOffsetMatrix<T,2>;
|
||||
|
||||
} //namespace
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef VTR_OSTREAM_GUARD_H
|
||||
#define VTR_OSTREAM_GUARD_H
|
||||
|
||||
namespace vtr {
|
||||
|
||||
//A RAII guard class to ensure restoration of output stream format
|
||||
class OsFormatGuard {
|
||||
public:
|
||||
explicit OsFormatGuard(std::ostream& os)
|
||||
: os_(os)
|
||||
, flags_(os_.flags()) //Save formatting flag state
|
||||
, width_(os_.width())
|
||||
, precision_(os.precision())
|
||||
, fill_(os.fill())
|
||||
{}
|
||||
|
||||
~OsFormatGuard() {
|
||||
os_.flags(flags_); //Restore
|
||||
os_.width(width_);
|
||||
os_.precision(precision_);
|
||||
os_.fill(fill_);
|
||||
}
|
||||
|
||||
OsFormatGuard(const OsFormatGuard&) = delete;
|
||||
OsFormatGuard& operator=(const OsFormatGuard&) = delete;
|
||||
OsFormatGuard(const OsFormatGuard&&) = delete;
|
||||
OsFormatGuard& operator=(const OsFormatGuard&&) = delete;
|
||||
private:
|
||||
std::ostream& os_;
|
||||
std::ios::fmtflags flags_;
|
||||
std::streamsize width_;
|
||||
std::streamsize precision_;
|
||||
char fill_;
|
||||
|
||||
};
|
||||
|
||||
} //namespace
|
||||
|
||||
#endif
|
|
@ -0,0 +1,57 @@
|
|||
#ifndef VTR_PAIR_UTIL_H
|
||||
#define VTR_PAIR_UTIL_H
|
||||
|
||||
#include "vtr_range.h"
|
||||
|
||||
namespace vtr {
|
||||
|
||||
//Iterator which derefernces the 'first' element of a std::pair iterator
|
||||
template<typename PairIter>
|
||||
class pair_first_iter {
|
||||
public:
|
||||
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = typename PairIter::value_type::first_type;
|
||||
using difference_type = void;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
pair_first_iter(PairIter init): iter_(init) {}
|
||||
auto operator++() { iter_++; return *this; }
|
||||
auto operator--() { iter_--; return *this; }
|
||||
auto operator*() { return iter_->first; }
|
||||
auto operator->() { return &iter_->first; }
|
||||
|
||||
friend bool operator== (const pair_first_iter lhs, const pair_first_iter rhs) { return lhs.iter_ == rhs.iter_; }
|
||||
friend bool operator!= (const pair_first_iter lhs, const pair_first_iter rhs) { return !(lhs == rhs); }
|
||||
|
||||
private:
|
||||
PairIter iter_;
|
||||
};
|
||||
|
||||
//Iterator which derefernces the 'second' element of a std::pair iterator
|
||||
template<typename PairIter>
|
||||
class pair_second_iter {
|
||||
public:
|
||||
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = typename PairIter::value_type::second_type;
|
||||
using difference_type = void;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
pair_second_iter(PairIter init): iter_(init) {}
|
||||
auto operator++() { iter_++; return *this; }
|
||||
auto operator--() { iter_--; return *this; }
|
||||
auto operator*() { return iter_->second; }
|
||||
auto operator->() { return &iter_->second; }
|
||||
|
||||
friend bool operator== (const pair_second_iter lhs, const pair_second_iter rhs) { return lhs.iter_ == rhs.iter_; }
|
||||
friend bool operator!= (const pair_second_iter lhs, const pair_second_iter rhs) { return !(lhs == rhs); }
|
||||
|
||||
private:
|
||||
PairIter iter_;
|
||||
};
|
||||
|
||||
} //namespace
|
||||
#endif
|
|
@ -0,0 +1,87 @@
|
|||
#include "vtr_path.h"
|
||||
|
||||
#include "vtr_util.h"
|
||||
|
||||
//TODO: currently this file assumes unix-like
|
||||
// in the future support windows
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace vtr {
|
||||
|
||||
const std::string PATH_DELIM = "/";
|
||||
|
||||
//Splits off the name and extension (including ".") of the specified filename
|
||||
std::array<std::string,2> split_ext(const std::string& filename) {
|
||||
std::array<std::string,2> name_ext;
|
||||
auto pos = filename.find_last_of('.');
|
||||
|
||||
if (pos == std::string::npos) {
|
||||
//No extension
|
||||
pos = filename.size();
|
||||
}
|
||||
|
||||
name_ext[0] = std::string(filename, 0, pos);
|
||||
name_ext[1] = std::string(filename, pos, filename.size() - pos);
|
||||
|
||||
return name_ext;
|
||||
}
|
||||
|
||||
std::string basename(const std::string& path) {
|
||||
auto elements = split(path, PATH_DELIM);
|
||||
|
||||
std::string str;
|
||||
if(elements.size() > 0) {
|
||||
//Return the last path element
|
||||
str = elements[elements.size() - 1];
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string dirname(const std::string& path) {
|
||||
auto elements = split(path, PATH_DELIM);
|
||||
|
||||
std::string str;
|
||||
if(elements.size() > 0) {
|
||||
//We need to start the dirname with a PATH_DELIM if path started with one
|
||||
if(starts_with(path, PATH_DELIM)) {
|
||||
str += PATH_DELIM;
|
||||
}
|
||||
|
||||
//Join all except the last path element
|
||||
str += join(elements.begin(), elements.end() - 1, PATH_DELIM);
|
||||
|
||||
//We append a final PATH_DELIM to allow clients to just append directly to the
|
||||
//returned value
|
||||
str += PATH_DELIM;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string getcwd() {
|
||||
constexpr size_t BUF_SIZE = 500;
|
||||
char buf [BUF_SIZE];
|
||||
|
||||
if (::getcwd(buf, BUF_SIZE)) {
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
//Check the global errno
|
||||
int error = errno;
|
||||
|
||||
switch (error) {
|
||||
case EACCES:
|
||||
throw std::runtime_error("Access denied");
|
||||
|
||||
default: {
|
||||
std::stringstream str;
|
||||
str << "Unrecognised error" << error;
|
||||
throw std::runtime_error(str.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef VTR_PATH_H
|
||||
#define VTR_PATH_H
|
||||
#include <string>
|
||||
#include <array>
|
||||
|
||||
namespace vtr {
|
||||
|
||||
//Splits off the name and extension (including ".") of the specified filename
|
||||
std::array<std::string,2> split_ext(const std::string& filename);
|
||||
|
||||
//Returns the basename of path (i.e. the last filename component)
|
||||
// For example, the path "/home/user/my_files/test.blif" -> "test.blif"
|
||||
std::string basename(const std::string& path);
|
||||
|
||||
//Returns the dirname of path (i.e. everything except the last filename component)
|
||||
// For example, the path "/home/user/my_files/test.blif" -> "/home/user/my_files/"
|
||||
std::string dirname(const std::string& path);
|
||||
|
||||
//Returns the current working directory
|
||||
std::string getcwd();
|
||||
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,76 @@
|
|||
#include <cstddef>
|
||||
|
||||
#include "vtr_random.h"
|
||||
#include "vtr_util.h"
|
||||
#include "vtr_error.h"
|
||||
|
||||
#define CHECK_RAND
|
||||
|
||||
namespace vtr {
|
||||
/* Portable random number generator defined below. Taken from ANSI C by *
|
||||
* K & R. Not a great generator, but fast, and good enough for my needs. */
|
||||
|
||||
constexpr size_t IA = 1103515245u;
|
||||
constexpr size_t IC = 12345u;
|
||||
constexpr size_t IM = 2147483648u;
|
||||
|
||||
static RandState random_state = 0;
|
||||
|
||||
void srandom(int seed) {
|
||||
random_state = (unsigned int) seed;
|
||||
}
|
||||
|
||||
/* returns the random_state value */
|
||||
RandState get_random_state() {
|
||||
return random_state;
|
||||
}
|
||||
|
||||
int irand(int imax, RandState& state) {
|
||||
|
||||
/* Creates a random integer between 0 and imax, inclusive. i.e. [0..imax] */
|
||||
int ival;
|
||||
|
||||
/* state = (state * IA + IC) % IM; */
|
||||
state = state * IA + IC; /* Use overflow to wrap */
|
||||
ival = state & (IM - 1); /* Modulus */
|
||||
ival = (int) ((float) ival * (float) (imax + 0.999) / (float) IM);
|
||||
|
||||
#ifdef CHECK_RAND
|
||||
if ((ival < 0) || (ival > imax)) {
|
||||
if (ival == imax + 1) {
|
||||
/* Due to random floating point rounding, sometimes above calculation gives number greater than ival by 1 */
|
||||
ival = imax;
|
||||
} else {
|
||||
throw VtrError(string_fmt("Bad value in my_irand, imax = %d ival = %d", imax, ival), __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return ival;
|
||||
}
|
||||
|
||||
int irand(int imax) {
|
||||
return irand(imax, random_state);
|
||||
}
|
||||
|
||||
float frand() {
|
||||
|
||||
/* Creates a random float between 0 and 1. i.e. [0..1). */
|
||||
|
||||
float fval;
|
||||
int ival;
|
||||
|
||||
random_state = random_state * IA + IC; /* Use overflow to wrap */
|
||||
ival = random_state & (IM - 1); /* Modulus */
|
||||
fval = (float) ival / (float) IM;
|
||||
|
||||
#ifdef CHECK_RAND
|
||||
if ((fval < 0) || (fval > 1.)) {
|
||||
throw VtrError(string_fmt("Bad value in my_frand, fval = %g", fval), __FILE__, __LINE__);
|
||||
}
|
||||
#endif
|
||||
|
||||
return (fval);
|
||||
}
|
||||
|
||||
} //namespace
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef VTR_RANDOM_H
|
||||
#define VTR_RANDOM_H
|
||||
#include <algorithm> //For std::swap
|
||||
|
||||
namespace vtr {
|
||||
/*********************** Portable random number generators *******************/
|
||||
typedef unsigned RandState;
|
||||
|
||||
void srandom(int seed);
|
||||
RandState get_random_state();
|
||||
int irand(int imax);
|
||||
int irand(int imax, RandState& rand_state);
|
||||
float frand();
|
||||
|
||||
//Portable/invariant version of std::shuffle
|
||||
//
|
||||
//Note that std::shuffle relies on std::uniform_int_distribution
|
||||
//which can produce different sequences accross different
|
||||
//compilers/compiler versions.
|
||||
//
|
||||
//This version should be deterministic/invariant. However, since
|
||||
//it uses vtr::irand(), may not be as well distributed as std::shuffle.
|
||||
template<typename Iter>
|
||||
void shuffle(Iter first, Iter last, RandState& rand_state) {
|
||||
for (auto i = (last - first) - 1; i > 0; --i) {
|
||||
using std::swap;
|
||||
swap(first[i], first[irand(i, rand_state)]);
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace
|
||||
#endif
|
|
@ -0,0 +1,73 @@
|
|||
#ifndef VTR_RANGE_H
|
||||
#define VTR_RANGE_H
|
||||
#include <iterator>
|
||||
|
||||
namespace vtr {
|
||||
/*
|
||||
* The vtr::Range template models a range defined by two iterators of type T.
|
||||
*
|
||||
* It allows conveniently returning a range from a single function call
|
||||
* without having to explicity expose the underlying container, or make two
|
||||
* explicit calls to retrieve the associated begin and end iterators.
|
||||
* It also enables the easy use of range-based-for loops.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* class My Data {
|
||||
* public:
|
||||
* typdef std::vector<int>::const_iterator my_iter;
|
||||
* vtr::Range<my_iter> data();
|
||||
* ...
|
||||
* private:
|
||||
* std::vector<int> data_;
|
||||
* };
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* MyDat my_data;
|
||||
*
|
||||
* //fill my_data
|
||||
*
|
||||
* for(int val : my_data.data()) {
|
||||
* //work with values stored in my_data
|
||||
* }
|
||||
*
|
||||
* The empty() and size() methods are convenience wrappers around the relevant
|
||||
* iterator comparisons.
|
||||
*
|
||||
* Note that size() is only constant time if T is a random-access iterator!
|
||||
*/
|
||||
template<typename T>
|
||||
class Range {
|
||||
public:
|
||||
Range(T b, T e): begin_(b), end_(e) {}
|
||||
T begin() { return begin_; }
|
||||
T end() { return end_; }
|
||||
bool empty() { return begin_ == end_; }
|
||||
size_t size() { return std::distance(begin_, end_); }
|
||||
private:
|
||||
T begin_;
|
||||
T end_;
|
||||
};
|
||||
|
||||
/*
|
||||
* Creates a vtr::Range from a pair of iterators.
|
||||
*
|
||||
* Unlike using the vtr::Range() constructor (which requires specifying
|
||||
* the template type T, using vtr::make_range() infers T from the arguments.
|
||||
*
|
||||
* Example usage:
|
||||
* auto my_range = vtr::make_range(my_vec.begin(), my_vec.end());
|
||||
*/
|
||||
template<typename T>
|
||||
auto make_range(T b, T e) { return Range<T>(b, e); }
|
||||
|
||||
/*
|
||||
* Creates a vtr::Range from a container
|
||||
*/
|
||||
template<typename Container>
|
||||
auto make_range(const Container& c) { return make_range(std::begin(c), std::end(c)); }
|
||||
|
||||
} //namespace
|
||||
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
#include "vtr_rusage.h"
|
||||
|
||||
#ifdef __unix__
|
||||
# include <sys/time.h>
|
||||
# include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
namespace vtr {
|
||||
|
||||
size_t get_max_rss() {
|
||||
size_t max_rss = 0;
|
||||
|
||||
#ifdef __unix__
|
||||
rusage usage;
|
||||
int result = getrusage(RUSAGE_SELF, &usage);
|
||||
|
||||
if (result == 0) { //Success
|
||||
//ru_maxrss is in kilobytes, convert to bytes
|
||||
max_rss = usage.ru_maxrss * 1024;
|
||||
}
|
||||
#else
|
||||
//Do nothing, other platform specific code could be added here
|
||||
//with appropriate defines
|
||||
#endif
|
||||
|
||||
return max_rss;
|
||||
}
|
||||
|
||||
} //namespace
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef VTR_RUSAGE_H
|
||||
#define VTR_RUSAGE_H
|
||||
#include <cstddef>
|
||||
|
||||
namespace vtr {
|
||||
|
||||
//Returns the maximum resident set size in bytes,
|
||||
//or zero if unable to determine.
|
||||
size_t get_max_rss();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef VTR_SENTINELS_H
|
||||
#define VTR_SENTINELS_H
|
||||
namespace vtr {
|
||||
|
||||
//Some specialized containers like vtr::linear_map and
|
||||
//vtr::vector_map require sentinel values to mark invalid/uninitialized
|
||||
//values. By convention, such containers query the sentinel objects static
|
||||
//INVALID() member function to retrieve the sentinel value.
|
||||
//
|
||||
//These classes allows users to specify a custom sentinel value.
|
||||
//
|
||||
//Usually the containers default to DefaultSentinel
|
||||
|
||||
//The sentinel value is the default constructed value of the type
|
||||
template<class T>
|
||||
class DefaultSentinel {
|
||||
public:
|
||||
constexpr static T INVALID() { return T(); }
|
||||
};
|
||||
|
||||
//Specialization for pointer types
|
||||
template<class T>
|
||||
class DefaultSentinel<T*> {
|
||||
public:
|
||||
constexpr static T* INVALID() { return nullptr; }
|
||||
};
|
||||
|
||||
//The sentile value is a specified value of the type
|
||||
template<class T, T val>
|
||||
class CustomSentinel {
|
||||
public:
|
||||
constexpr static T INVALID() { return T(val); }
|
||||
};
|
||||
|
||||
//The common case where -1 is used as the sentinel value
|
||||
template<class T>
|
||||
using MinusOneSentinel = CustomSentinel<T,-1>;
|
||||
|
||||
} //namespace
|
||||
#endif
|
|
@ -0,0 +1,233 @@
|
|||
#ifndef VTR_STRONG_ID_H
|
||||
#define VTR_STRONG_ID_H
|
||||
/*
|
||||
* This header provides the StrongId class, a template which can be used to
|
||||
* create strong Id's which avoid accidental type conversions (generating
|
||||
* compiler errors when they occur).
|
||||
*
|
||||
* Motivation
|
||||
* ==========
|
||||
* It is common to use an Id (typically an integer) to identify and represent a component.
|
||||
* A basic example (poor style):
|
||||
*
|
||||
* size_t count_net_terminals(int net_id);
|
||||
*
|
||||
* Where a plain int is used to represent the net identifier.
|
||||
* Using a plain basic type is poor style since it makes it unclear that the parameter is
|
||||
* an Id.
|
||||
*
|
||||
* A better example is to use a typedef:
|
||||
*
|
||||
* typedef int NetId;
|
||||
*
|
||||
* size_t count_net_teriminals(NetId net_id);
|
||||
*
|
||||
* It is now clear that the parameter is expecting an Id.
|
||||
*
|
||||
* However this approach has some limitations. In particular, typedef's only create type
|
||||
* aliases, and still allow conversions. This is problematic if there are multiple types
|
||||
* of Ids. For example:
|
||||
*
|
||||
* typedef int NetId;
|
||||
* typedef int BlkId;
|
||||
*
|
||||
* size_t count_net_teriminals(NetId net_id);
|
||||
*
|
||||
* BlkId blk_id = 10;
|
||||
* NetId net_id = 42;
|
||||
*
|
||||
* count_net_teriminals(net_id); //OK
|
||||
* count_net_teriminals(blk_id); //Bug: passed a BlkId as a NetId
|
||||
*
|
||||
* Since typdefs are aliases the compiler issues no errors or warnings, and silently passes
|
||||
* the BlkId where a NetId is expected. This results in hard to diagnose bugs.
|
||||
*
|
||||
* We can avoid this issue by using a StrongId:
|
||||
*
|
||||
* struct net_id_tag; //Phantom tag for NetId
|
||||
* struct blk_id_tag; //Phantom tag for BlkId
|
||||
*
|
||||
* typedef StrongId<net_id_tag> NetId;
|
||||
* typedef StrongId<blk_id_tag> BlkId;
|
||||
*
|
||||
* size_t count_net_teriminals(NetId net_id);
|
||||
*
|
||||
* BlkId blk_id = 10;
|
||||
* NetId net_id = 42;
|
||||
*
|
||||
* count_net_teriminals(net_id); //OK
|
||||
* count_net_teriminals(blk_id); //Compiler Error: NetId expected!
|
||||
*
|
||||
* StrongId is a template which implements the basic features of an Id, but disallows silent conversions
|
||||
* between different types of Ids. It uses another 'tag' type (passed as the first template parameter)
|
||||
* to uniquely identify the type of the Id (preventing conversions between different types of Ids).
|
||||
*
|
||||
* Usage
|
||||
* =====
|
||||
*
|
||||
* The StrongId template class takes one required and three optional template parameters:
|
||||
*
|
||||
* 1) Tag - the unique type used to identify this type of Ids [Required]
|
||||
* 2) T - the underlying integral id type (default: int) [Optional]
|
||||
* 3) T sentinel - a value representing an invalid Id (default: -1) [Optional]
|
||||
*
|
||||
* If no value is supllied during construction the StrongId is initialized to the invalid/sentinel value.
|
||||
*
|
||||
* Example 1: default definition
|
||||
*
|
||||
* struct net_id_tag;
|
||||
* typedef StrongId<net_id_tag> NetId; //Internally stores an integer Id, -1 represents invalid
|
||||
*
|
||||
* Example 2: definition with custom underlying type
|
||||
*
|
||||
* struct blk_id_tag;
|
||||
* typedef StrongId<net_id_tag,size_t> BlkId; //Internally stores a size_t Id, -1 represents invalid
|
||||
*
|
||||
* Example 3: definition with custom underlying type and custom sentinel value
|
||||
*
|
||||
* struct pin_id_tag;
|
||||
* typedef StrongId<net_id_tag,size_t,0> PinId; //Internally stores a size_t Id, 0 represents invalid
|
||||
*
|
||||
* Example 4: Creating Ids
|
||||
*
|
||||
* struct net_id_tag;
|
||||
* typedef StrongId<net_id_tag> MyId; //Internally stores an integer Id, -1 represents invalid
|
||||
*
|
||||
* MyId my_id; //Defaults to the sentinel value (-1 by default)
|
||||
* MyId my_other_id = 5; //Explicit construction
|
||||
* MyId my_thrid_id(25); //Explicit construction
|
||||
*
|
||||
* Example 5: Comparing Ids
|
||||
*
|
||||
* struct net_id_tag;
|
||||
* typedef StrongId<net_id_tag> MyId; //Internally stores an integer Id, -1 represents invalid
|
||||
*
|
||||
* MyId my_id; //Defaults to the sentinel value (-1 by default)
|
||||
* MyId my_id_one = 1;
|
||||
* MyId my_id_two = 2;
|
||||
* MyId my_id_also_one = 1;
|
||||
*
|
||||
* my_id_one == my_id_also_one; //True
|
||||
* my_id_one == my_id; //False
|
||||
* my_id_one == my_id_two; //False
|
||||
* my_id_one != my_id_two; //True
|
||||
*
|
||||
* Example 5: Checking for invalid Ids
|
||||
*
|
||||
* struct net_id_tag;
|
||||
* typedef StrongId<net_id_tag> MyId; //Internally stores an integer Id, -1 represents invalid
|
||||
*
|
||||
* MyId my_id; //Defaults to the sentinel value
|
||||
* MyId my_id_one = 1;
|
||||
*
|
||||
* //Comparison against a constructed invalid id
|
||||
* my_id == MyId::INVALID(); //True
|
||||
* my_id_one == MyId::INVALID(); //False
|
||||
* my_id_one != MyId::INVALID(); //True
|
||||
*
|
||||
* //The Id can also be evaluated in a boolean context against the sentinel value
|
||||
* if(my_id) //False, my_id is invalid
|
||||
* if(!my_id) //True my_id is valid
|
||||
* if(my_id_one) //True my_id_one is valid
|
||||
*
|
||||
* Example 6: Indexing data structures
|
||||
*
|
||||
* struct my_id_tag;
|
||||
* typedef StrongId<net_id_tag> MyId; //Internally stores an integer Id, -1 represents invalid
|
||||
*
|
||||
* std::vector<int> my_vec = {0, 1, 2, 3, 4, 5};
|
||||
*
|
||||
* MyId my_id = 2;
|
||||
*
|
||||
* my_vec[size_t(my_id)]; //Access the third element via explicit conversion
|
||||
*/
|
||||
#include <type_traits> //for std::is_integral
|
||||
#include <cstddef> //for std::size_t
|
||||
#include <functional> //for std::hash
|
||||
|
||||
namespace vtr {
|
||||
|
||||
//Forward declare the class (needed for operator declarations)
|
||||
template<typename tag, typename T, T sentinel>
|
||||
class StrongId;
|
||||
|
||||
//Forward declare the equality/inequality operators
|
||||
// We need to do this before the class definition so the class can
|
||||
// friend them
|
||||
template<typename tag, typename T, T sentinel>
|
||||
bool operator==(const StrongId<tag,T,sentinel>& lhs, const StrongId<tag,T,sentinel>& rhs);
|
||||
|
||||
template<typename tag, typename T, T sentinel>
|
||||
bool operator!=(const StrongId<tag,T,sentinel>& lhs, const StrongId<tag,T,sentinel>& rhs);
|
||||
|
||||
template<typename tag, typename T, T sentinel>
|
||||
bool operator<(const StrongId<tag,T,sentinel>& lhs, const StrongId<tag,T,sentinel>& rhs);
|
||||
|
||||
|
||||
//Class template definition with default template parameters
|
||||
template<typename tag, typename T=int, T sentinel=T(-1)>
|
||||
class StrongId {
|
||||
static_assert(std::is_integral<T>::value, "T must be integral");
|
||||
|
||||
public:
|
||||
//Gets the invalid Id
|
||||
static constexpr StrongId INVALID() { return StrongId(); }
|
||||
|
||||
//Default to the sentinel value
|
||||
constexpr StrongId() : id_(sentinel) {}
|
||||
|
||||
//Only allow explict constructions from a raw Id (no automatic conversions)
|
||||
explicit constexpr StrongId(T id): id_(id) {}
|
||||
|
||||
//Allow some explicit conversion to useful types
|
||||
|
||||
//Allow explicit conversion to bool (e.g. if(id))
|
||||
explicit operator bool() const { return *this != INVALID(); }
|
||||
|
||||
//Allow explicit conversion to size_t (e.g. my_vector[size_t(strong_id)])
|
||||
explicit operator std::size_t() const { return static_cast<std::size_t>(id_); }
|
||||
|
||||
|
||||
//To enable hasing Ids
|
||||
friend std::hash<StrongId<tag,T,sentinel>>;
|
||||
|
||||
//To enable comparisions between Ids
|
||||
// Note that since these are templated functions we provide an empty set of template parameters
|
||||
// after the function name (i.e. <>)
|
||||
friend bool operator== <>(const StrongId<tag,T,sentinel>& lhs, const StrongId<tag,T,sentinel>& rhs);
|
||||
friend bool operator!= <>(const StrongId<tag,T,sentinel>& lhs, const StrongId<tag,T,sentinel>& rhs);
|
||||
friend bool operator< <>(const StrongId<tag,T,sentinel>& lhs, const StrongId<tag,T,sentinel>& rhs);
|
||||
private:
|
||||
T id_;
|
||||
};
|
||||
|
||||
template<typename tag, typename T, T sentinel>
|
||||
bool operator==(const StrongId<tag,T,sentinel>& lhs, const StrongId<tag,T,sentinel>& rhs) {
|
||||
return lhs.id_ == rhs.id_;
|
||||
}
|
||||
|
||||
template<typename tag, typename T, T sentinel>
|
||||
bool operator!=(const StrongId<tag,T,sentinel>& lhs, const StrongId<tag,T,sentinel>& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
//Needed for std::map-like containers
|
||||
template<typename tag, typename T, T sentinel>
|
||||
bool operator<(const StrongId<tag,T,sentinel>& lhs, const StrongId<tag,T,sentinel>& rhs) {
|
||||
return lhs.id_ < rhs.id_;
|
||||
}
|
||||
|
||||
} //namespace vtr
|
||||
|
||||
//Specialize std::hash for StrongId's (needed for std::unordered_map-like containers)
|
||||
namespace std {
|
||||
template<typename tag, typename T, T sentinel>
|
||||
struct hash<vtr::StrongId<tag,T,sentinel>> {
|
||||
std::size_t operator()(const vtr::StrongId<tag,T,sentinel> k) const noexcept {
|
||||
return std::hash<T>()(k.id_); //Hash with the underlying type
|
||||
}
|
||||
};
|
||||
} //namespace std
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,85 @@
|
|||
#include "vtr_time.h"
|
||||
|
||||
#include "vtr_log.h"
|
||||
#include "vtr_rusage.h"
|
||||
|
||||
namespace vtr {
|
||||
|
||||
int f_timer_depth = 0;
|
||||
|
||||
Timer::Timer()
|
||||
: start_(clock::now())
|
||||
, initial_max_rss_(get_max_rss()) {
|
||||
}
|
||||
|
||||
float Timer::elapsed_sec() const {
|
||||
return std::chrono::duration<float>(clock::now() - start_).count();
|
||||
}
|
||||
|
||||
float Timer::max_rss_mib() const {
|
||||
return get_max_rss() / BYTE_TO_MIB;
|
||||
}
|
||||
|
||||
float Timer::delta_max_rss_mib() const {
|
||||
return (get_max_rss() - initial_max_rss_) / BYTE_TO_MIB;
|
||||
}
|
||||
|
||||
ScopedActionTimer::ScopedActionTimer(std::string action_str)
|
||||
: action_(action_str)
|
||||
, depth_(f_timer_depth++) {
|
||||
}
|
||||
|
||||
ScopedActionTimer::~ScopedActionTimer() {
|
||||
--f_timer_depth;
|
||||
}
|
||||
|
||||
void ScopedActionTimer::quiet(bool value) {
|
||||
quiet_ = value;
|
||||
}
|
||||
|
||||
bool ScopedActionTimer::quiet() const {
|
||||
return quiet_;
|
||||
}
|
||||
|
||||
std::string ScopedActionTimer::action() const {
|
||||
return action_;
|
||||
}
|
||||
|
||||
std::string ScopedActionTimer::pad() const {
|
||||
if (depth() == 0) {
|
||||
return "";
|
||||
}
|
||||
return std::string(depth(), '#') + " ";
|
||||
}
|
||||
|
||||
int ScopedActionTimer::depth() const {
|
||||
return depth_;
|
||||
}
|
||||
|
||||
|
||||
ScopedFinishTimer::ScopedFinishTimer(std::string action_str)
|
||||
: ScopedActionTimer(action_str) {
|
||||
}
|
||||
|
||||
ScopedFinishTimer::~ScopedFinishTimer() {
|
||||
if (!quiet()) {
|
||||
vtr::printf_info("%s%s took %.2f seconds (max_rss %.1f MiB)\n",
|
||||
pad().c_str(), action().c_str(), elapsed_sec(),
|
||||
max_rss_mib());
|
||||
}
|
||||
}
|
||||
|
||||
ScopedStartFinishTimer::ScopedStartFinishTimer(std::string action_str)
|
||||
: ScopedActionTimer(action_str) {
|
||||
vtr::printf_info("%s%s\n", pad().c_str(), action().c_str());
|
||||
}
|
||||
|
||||
ScopedStartFinishTimer::~ScopedStartFinishTimer() {
|
||||
if (!quiet()) {
|
||||
vtr::printf_info("%s%s took %.2f seconds (max_rss %.1f MiB, delta_rss %+.1f MiB)\n",
|
||||
pad().c_str(), action().c_str(), elapsed_sec(),
|
||||
max_rss_mib(), delta_max_rss_mib());
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace
|
|
@ -0,0 +1,94 @@
|
|||
#ifndef VTR_TIME_H
|
||||
#define VTR_TIME_H
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
namespace vtr {
|
||||
|
||||
//Class for tracking time elapsed since construction
|
||||
class Timer {
|
||||
public:
|
||||
Timer();
|
||||
virtual ~Timer() = default;
|
||||
|
||||
//No copy
|
||||
Timer(Timer&) = delete;
|
||||
Timer& operator=(Timer&) = delete;
|
||||
|
||||
//No move
|
||||
Timer(Timer&&) = delete;
|
||||
Timer& operator=(Timer&&) = delete;
|
||||
|
||||
//Return elapsed time in seconds
|
||||
float elapsed_sec() const;
|
||||
|
||||
//Return peak memory resident set size (in MiB)
|
||||
float max_rss_mib() const;
|
||||
|
||||
//Return change in peak memory resident set size (in MiB)
|
||||
float delta_max_rss_mib() const;
|
||||
private:
|
||||
using clock = std::chrono::steady_clock;
|
||||
std::chrono::time_point<clock> start_;
|
||||
|
||||
size_t initial_max_rss_; //Maximum resident set size In bytes
|
||||
constexpr static float BYTE_TO_MIB = 1024*1024;
|
||||
};
|
||||
|
||||
class ScopedActionTimer : public Timer {
|
||||
public:
|
||||
ScopedActionTimer(const std::string action);
|
||||
~ScopedActionTimer();
|
||||
|
||||
void quiet(bool value);
|
||||
bool quiet() const;
|
||||
std::string action() const;
|
||||
|
||||
protected:
|
||||
int depth() const;
|
||||
std::string pad() const;
|
||||
|
||||
private:
|
||||
const std::string action_;
|
||||
bool quiet_ = false;
|
||||
int depth_;
|
||||
};
|
||||
|
||||
//Scoped elapsed time class which prints the time elapsed for
|
||||
//the specified action when it is destructed.
|
||||
//
|
||||
//For example:
|
||||
//
|
||||
// {
|
||||
// vtr::ScopedFinishTimer timer("my_action");
|
||||
//
|
||||
// //Do other work
|
||||
//
|
||||
// //Will print: 'my_action took X.XX seconds' when out-of-scope
|
||||
// }
|
||||
class ScopedFinishTimer : public ScopedActionTimer {
|
||||
public:
|
||||
ScopedFinishTimer(const std::string action);
|
||||
~ScopedFinishTimer();
|
||||
};
|
||||
|
||||
//Scoped elapsed time class which prints out the action when
|
||||
//initialized and again both the action and elapsed time
|
||||
//when destructed.
|
||||
//For example:
|
||||
//
|
||||
// {
|
||||
// vtr::ScopedStartFinishTimer timer("my_action") //Will print: 'my_action'
|
||||
//
|
||||
// //Do other work
|
||||
//
|
||||
// //Will print 'my_action took X.XX seconds' when out of scope
|
||||
// }
|
||||
class ScopedStartFinishTimer : public ScopedActionTimer {
|
||||
public:
|
||||
ScopedStartFinishTimer(const std::string action);
|
||||
~ScopedStartFinishTimer();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,387 @@
|
|||
#include <cstdarg>
|
||||
#include <cstdlib>
|
||||
#include <cerrno> //For errno
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include "vtr_util.h"
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_memory.h"
|
||||
#include "vtr_error.h"
|
||||
|
||||
|
||||
namespace vtr {
|
||||
|
||||
|
||||
std::string out_file_prefix; /* used by fopen */
|
||||
static int file_line_number = 0; /* file in line number being parsed (used by fgets) */
|
||||
static int cont; /* line continued? (used by strtok)*/
|
||||
|
||||
|
||||
//Splits the string 'text' along the specified delimiter characters in 'delims'
|
||||
//The split strings (excluding the delimiters) are returned
|
||||
std::vector<std::string> split(const char* text, const std::string delims) {
|
||||
if(text) {
|
||||
std::string text_str(text);
|
||||
return split(text_str, delims);
|
||||
}
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string& text, const std::string delims) {
|
||||
std::vector<std::string> tokens;
|
||||
|
||||
std::string curr_tok;
|
||||
for(char c : text) {
|
||||
if(delims.find(c) != std::string::npos) {
|
||||
//Delimeter character
|
||||
if(!curr_tok.empty()) {
|
||||
//At the end of the token
|
||||
|
||||
//Save it
|
||||
tokens.push_back(curr_tok);
|
||||
|
||||
//Reset token
|
||||
curr_tok.clear();
|
||||
} else {
|
||||
//Pass
|
||||
}
|
||||
} else {
|
||||
//Non-delimeter append to token
|
||||
curr_tok += c;
|
||||
}
|
||||
}
|
||||
|
||||
//Add last token
|
||||
if(!curr_tok.empty()) {
|
||||
//Save it
|
||||
tokens.push_back(curr_tok);
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
std::string replace_first(const std::string& input, const std::string& search, const std::string& replace) {
|
||||
auto pos = input.find(search);
|
||||
|
||||
std::string output(input, 0, pos);
|
||||
output += replace;
|
||||
output += std::string(input, pos + search.size());
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string replace_all(const std::string& input, const std::string& search, const std::string& replace) {
|
||||
|
||||
std::string output;
|
||||
|
||||
size_t last = 0;
|
||||
size_t pos = input.find(search, last); //Find the first instance of 'search' starting at or after 'last'
|
||||
while(pos != std::string::npos) {
|
||||
output += input.substr(last, pos - last); //Append anything in the input string between last and current match
|
||||
output += replace; //Add the replacement
|
||||
|
||||
last = pos + search.size(); //Advance past the current match
|
||||
|
||||
pos = input.find(search, last); //Look for the next match
|
||||
}
|
||||
output += input.substr(last, pos - last); //Append anything in 'input' after the last match
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
//Retruns true if str starts with prefix
|
||||
bool starts_with(std::string str, std::string prefix) {
|
||||
return str.find(prefix) == 0;
|
||||
}
|
||||
|
||||
//Returns a std::string formatted using a printf-style format string
|
||||
std::string string_fmt(const char* fmt, ...) {
|
||||
// Make a variable argument list
|
||||
va_list va_args;
|
||||
|
||||
// Initialize variable argument list
|
||||
va_start(va_args, fmt);
|
||||
|
||||
//Format string
|
||||
std::string str = vstring_fmt(fmt, va_args);
|
||||
|
||||
// Reset variable argument list
|
||||
va_end(va_args);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
//Returns a std::string formatted using a printf-style format string taking
|
||||
//an explicit va_list
|
||||
std::string vstring_fmt(const char* fmt, va_list args) {
|
||||
|
||||
// We need to copy the args so we don't change them before the true formating
|
||||
va_list va_args_copy;
|
||||
va_copy(va_args_copy, args);
|
||||
|
||||
//Determine the formatted length using a copy of the args
|
||||
int len = std::vsnprintf(nullptr, 0, fmt, va_args_copy);
|
||||
|
||||
va_end(va_args_copy); //Clean-up
|
||||
|
||||
//Negative if there is a problem with the format string
|
||||
VTR_ASSERT_MSG(len >= 0, "Problem decoding format string");
|
||||
|
||||
size_t buf_size = len + 1; //For terminator
|
||||
|
||||
//Allocate a buffer
|
||||
// unique_ptr will free buffer automatically
|
||||
std::unique_ptr<char[]> buf(new char[buf_size]);
|
||||
|
||||
//Format into the buffer using the original args
|
||||
len = std::vsnprintf(buf.get(), buf_size, fmt, args);
|
||||
|
||||
VTR_ASSERT_MSG(len >= 0, "Problem decoding format string");
|
||||
VTR_ASSERT(static_cast<size_t>(len) == buf_size - 1);
|
||||
|
||||
//Build the string from the buffer
|
||||
return std::string(buf.get(), len);
|
||||
}
|
||||
|
||||
/* An alternate for strncpy since strncpy doesn't work as most
|
||||
* people would expect. This ensures null termination */
|
||||
char* strncpy(char *dest, const char *src, size_t size) {
|
||||
/* Find string's length */
|
||||
size_t len = std::strlen(src);
|
||||
|
||||
/* Cap length at (num - 1) to leave room for \0 */
|
||||
if (size <= len)
|
||||
len = (size - 1);
|
||||
|
||||
/* Copy as much of string as we can fit */
|
||||
std::memcpy(dest, src, len);
|
||||
|
||||
/* explicit null termination */
|
||||
dest[len] = '\0';
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
char* strdup(const char *str) {
|
||||
|
||||
if (str == nullptr ) {
|
||||
return nullptr ;
|
||||
}
|
||||
|
||||
size_t Len = std::strlen(str);
|
||||
//use calloc to already make the last char '\0'
|
||||
return (char *)std::memcpy(vtr::calloc(Len+1, sizeof(char)), str, Len);;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T atoT(const std::string& value, const std::string& type_name) {
|
||||
//The c version of atof doesn't catch errors.
|
||||
//
|
||||
//This version uses stringstream to detect conversion errors
|
||||
std::istringstream ss(value);
|
||||
|
||||
T val;
|
||||
ss >> val;
|
||||
|
||||
if(ss.fail() || !ss.eof()) {
|
||||
//Failed to convert, or did not consume all input
|
||||
std::stringstream msg;
|
||||
msg << "Failed to convert string '" << value << "' to " << type_name;
|
||||
throw VtrError(msg.str(), __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int atoi(const std::string& value) {
|
||||
return atoT<int>(value, "int");
|
||||
}
|
||||
|
||||
double atod(const std::string& value) {
|
||||
return atoT<double>(value, "double");
|
||||
}
|
||||
|
||||
float atof(const std::string& value) {
|
||||
return atoT<float>(value, "float");
|
||||
}
|
||||
|
||||
unsigned atou(const std::string& value) {
|
||||
return atoT<unsigned>(value, "unsigned int");
|
||||
}
|
||||
|
||||
char* strtok(char *ptr, const char *tokens, FILE * fp, char *buf) {
|
||||
|
||||
/* Get next token, and wrap to next line if \ at end of line. *
|
||||
* There is a bit of a "gotcha" in strtok. It does not make a *
|
||||
* copy of the character array which you pass by pointer on the *
|
||||
* first call. Thus, you must make sure this array exists for *
|
||||
* as long as you are using strtok to parse that line. Don't *
|
||||
* use local buffers in a bunch of subroutines calling each *
|
||||
* other; the local buffer may be overwritten when the stack is *
|
||||
* restored after return from the subroutine. */
|
||||
|
||||
char *val;
|
||||
|
||||
val = std::strtok(ptr, tokens);
|
||||
for (;;) {
|
||||
if (val != nullptr || cont == 0)
|
||||
return (val);
|
||||
|
||||
/* return unless we have a null value and a continuation line */
|
||||
if (vtr::fgets(buf, bufsize, fp) == nullptr )
|
||||
return (nullptr );
|
||||
|
||||
val = std::strtok(buf, tokens);
|
||||
}
|
||||
}
|
||||
|
||||
FILE* fopen(const char *fname, const char *flag) {
|
||||
FILE *fp;
|
||||
size_t Len;
|
||||
char *new_fname = nullptr;
|
||||
file_line_number = 0;
|
||||
|
||||
/* Appends a prefix string for output files */
|
||||
if (!out_file_prefix.empty()) {
|
||||
if (std::strchr(flag, 'w')) {
|
||||
Len = 1; /* NULL char */
|
||||
Len += std::strlen(out_file_prefix.c_str());
|
||||
Len += std::strlen(fname);
|
||||
new_fname = (char *) vtr::malloc(Len * sizeof(char));
|
||||
strcpy(new_fname, out_file_prefix.c_str());
|
||||
strcat(new_fname, fname);
|
||||
fname = new_fname;
|
||||
}
|
||||
}
|
||||
|
||||
if (nullptr == (fp = std::fopen(fname, flag))) {
|
||||
throw VtrError(string_fmt("Error opening file %s for %s access: %s.\n", fname, flag, strerror(errno)), __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
if (new_fname)
|
||||
std::free(new_fname);
|
||||
|
||||
return (fp);
|
||||
}
|
||||
|
||||
int fclose(FILE* f) {
|
||||
return std::fclose(f);
|
||||
}
|
||||
|
||||
char* fgets(char *buf, int max_size, FILE * fp) {
|
||||
/* Get an input line, update the line number and cut off *
|
||||
* any comment part. A \ at the end of a line with no *
|
||||
* comment part (#) means continue. vtr::fgets should give *
|
||||
* identical results for Windows (\r\n) and Linux (\n) *
|
||||
* newlines, since it replaces each carriage return \r *
|
||||
* by a newline character \n. Returns NULL after EOF. */
|
||||
|
||||
int ch;
|
||||
int i;
|
||||
|
||||
cont = 0; /* line continued? */
|
||||
file_line_number++; /* global variable */
|
||||
|
||||
for (i = 0; i < max_size - 1; i++) { /* Keep going until the line finishes or the buffer is full */
|
||||
|
||||
ch = std::fgetc(fp);
|
||||
|
||||
if (std::feof(fp)) { /* end of file */
|
||||
if (i == 0) {
|
||||
return nullptr ; /* required so we can write while (vtr::fgets(...) != NULL) */
|
||||
} else { /* no newline before end of file - last line must be returned */
|
||||
buf[i] = '\0';
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == '#') { /* comment */
|
||||
buf[i] = '\0';
|
||||
while ((ch = std::fgetc(fp)) != '\n' && !std::feof(fp))
|
||||
; /* skip the rest of the line */
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (ch == '\r' || ch == '\n') { /* newline (cross-platform) */
|
||||
if (i != 0 && buf[i - 1] == '\\') { /* if \ at end of line, line continued */
|
||||
cont = 1;
|
||||
buf[i - 1] = '\n'; /* May need this for tokens */
|
||||
buf[i] = '\0';
|
||||
} else {
|
||||
buf[i] = '\n';
|
||||
buf[i + 1] = '\0';
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
buf[i] = ch; /* copy character into the buffer */
|
||||
|
||||
}
|
||||
|
||||
/* Buffer is full but line has not terminated, so error */
|
||||
throw VtrError(string_fmt("Error on line %d -- line is too long for input buffer.\n"
|
||||
"All lines must be at most %d characters long.\n", bufsize - 2),
|
||||
__FILE__, __LINE__);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns line number of last opened and read file
|
||||
*/
|
||||
int get_file_line_number_of_last_opened_file() {
|
||||
return file_line_number;
|
||||
}
|
||||
|
||||
|
||||
bool file_exists(const char* filename) {
|
||||
FILE * file;
|
||||
|
||||
if (filename == nullptr ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
file = std::fopen(filename, "r");
|
||||
if (file) {
|
||||
std::fclose(file);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Date:July 17th, 2013
|
||||
* Author: Daniel Chen
|
||||
* Purpose: Checks the file extension of an file to ensure
|
||||
* correct file format. Returns true if format is
|
||||
* correct, and false otherwise.
|
||||
* Note: This is probably a fragile check, but at least
|
||||
* should prevent common problems such as swapping
|
||||
* architecture file and blif file on the VPR
|
||||
* command line.
|
||||
*/
|
||||
|
||||
bool check_file_name_extension(const char* file_name,
|
||||
const char* file_extension){
|
||||
const char* str;
|
||||
int len_extension;
|
||||
|
||||
len_extension = std::strlen(file_extension);
|
||||
str = std::strstr(file_name, file_extension);
|
||||
if(str == nullptr || (*(str + len_extension) != '\0')){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> ReadLineTokens(FILE * InFile, int *LineNum) {
|
||||
std::unique_ptr<char[]> buf(new char[vtr::bufsize]);
|
||||
|
||||
const char* line = vtr::fgets(buf.get(), vtr::bufsize, InFile);
|
||||
|
||||
++(*LineNum);
|
||||
|
||||
return vtr::split(line);
|
||||
}
|
||||
|
||||
} //namespace
|
|
@ -0,0 +1,102 @@
|
|||
#ifndef VTR_UTIL_H
|
||||
#define VTR_UTIL_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdarg>
|
||||
#include <array>
|
||||
|
||||
namespace vtr {
|
||||
|
||||
//Splits the string 'text' along the specified delimiter characters in 'delims'
|
||||
//The split strings (excluding the delimiters) are returned
|
||||
std::vector<std::string> split(const char* text, const std::string delims=" \t\n");
|
||||
std::vector<std::string> split(const std::string& text, const std::string delims=" \t\n");
|
||||
|
||||
//Returns 'input' with the first instance of 'search' replaced with 'replace'
|
||||
std::string replace_first(const std::string& input, const std::string& search, const std::string& replace);
|
||||
|
||||
//Returns 'input' with all instances of 'search' replaced with 'replace'
|
||||
std::string replace_all(const std::string& input, const std::string& search, const std::string& replace);
|
||||
|
||||
//Retruns true if str starts with prefix
|
||||
bool starts_with(std::string str, std::string prefix);
|
||||
|
||||
//Returns a std::string formatted using a printf-style format string
|
||||
std::string string_fmt(const char* fmt, ...);
|
||||
|
||||
//Returns a std::string formatted using a printf-style format string taking
|
||||
//an explicit va_list
|
||||
std::string vstring_fmt(const char* fmt, va_list args);
|
||||
|
||||
//Joins a sequence by a specified delimeter
|
||||
// For example the sequence {"home", "user", "my_files", "test.blif"} with delim="/"
|
||||
// would return "home/user/my_files/test.blif"
|
||||
template<typename Iter>
|
||||
std::string join(Iter begin, Iter end, std::string delim);
|
||||
|
||||
template<typename Container>
|
||||
std::string join(Container container, std::string delim);
|
||||
|
||||
template<typename T>
|
||||
std::string join(std::initializer_list<T> list, std::string delim);
|
||||
|
||||
/*
|
||||
* Legacy c-style function replacements, typically these add extra error checking
|
||||
* and/or correct 'unexpected' behaviour of the standard c-functions
|
||||
*/
|
||||
constexpr size_t bufsize = 32768; /* Maximum line length for various parsing proc. */
|
||||
char* strncpy(char *dest, const char *src, size_t size);
|
||||
char* strdup(const char *str);
|
||||
char* strtok(char *ptr, const char *tokens, FILE * fp, char *buf);
|
||||
FILE* fopen(const char *fname, const char *flag);
|
||||
int fclose(FILE* f);
|
||||
char* fgets(char *buf, int max_size, FILE * fp);
|
||||
|
||||
int atoi(const std::string& value);
|
||||
unsigned atou(const std::string& value);
|
||||
float atof(const std::string& value);
|
||||
double atod(const std::string& value);
|
||||
|
||||
/*
|
||||
* File utilities
|
||||
*/
|
||||
int get_file_line_number_of_last_opened_file();
|
||||
bool file_exists(const char * filename);
|
||||
bool check_file_name_extension(const char* file_name,
|
||||
const char* file_extension);
|
||||
|
||||
extern std::string out_file_prefix;
|
||||
|
||||
/*
|
||||
* Legacy ReadLine Tokening
|
||||
*/
|
||||
std::vector<std::string> ReadLineTokens(FILE * InFile, int *LineNum);
|
||||
|
||||
/*
|
||||
* Template implementations
|
||||
*/
|
||||
template<typename Iter>
|
||||
std::string join(Iter begin, Iter end, std::string delim) {
|
||||
std::string joined_str;
|
||||
for(auto iter = begin; iter != end; ++iter) {
|
||||
joined_str += *iter;
|
||||
if(iter != end - 1) {
|
||||
joined_str += delim;
|
||||
}
|
||||
}
|
||||
return joined_str;
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
std::string join(Container container, std::string delim) {
|
||||
return join(std::begin(container), std::end(container), delim);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string join(std::initializer_list<T> list, std::string delim) {
|
||||
return join(list.begin(), list.end(), delim);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,146 @@
|
|||
#ifndef VTR_VECTOR
|
||||
#define VTR_VECTOR
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include "vtr_range.h"
|
||||
|
||||
namespace vtr {
|
||||
|
||||
//A std::vector container which is indexed by K (instead of size_t).
|
||||
//
|
||||
//The main use of this container is to behave like a std::vector which is
|
||||
//indexed by a vtr::StrongId. It assumes that K is explicitly convertable to size_t
|
||||
//(i.e. via operator size_t()), and can be explicitly constructed from a size_t.
|
||||
//
|
||||
//If you need more std::map-like (instead of std::vector-like) behaviour see
|
||||
//vtr::vector_map.
|
||||
template<typename K, typename V>
|
||||
class vector : private std::vector<V> {
|
||||
public:
|
||||
typedef K key_type;
|
||||
|
||||
class key_iterator;
|
||||
typedef vtr::Range<key_iterator> key_range;
|
||||
|
||||
public:
|
||||
//Pass through std::vector's types
|
||||
using typename std::vector<V>::value_type;
|
||||
using typename std::vector<V>::allocator_type;
|
||||
using typename std::vector<V>::reference;
|
||||
using typename std::vector<V>::const_reference;
|
||||
using typename std::vector<V>::pointer;
|
||||
using typename std::vector<V>::const_pointer;
|
||||
using typename std::vector<V>::iterator;
|
||||
using typename std::vector<V>::const_iterator;
|
||||
using typename std::vector<V>::reverse_iterator;
|
||||
using typename std::vector<V>::const_reverse_iterator;
|
||||
using typename std::vector<V>::difference_type;
|
||||
using typename std::vector<V>::size_type;
|
||||
|
||||
//Pass through std::vector's methods
|
||||
using std::vector<V>::vector;
|
||||
|
||||
using std::vector<V>::begin;
|
||||
using std::vector<V>::end;
|
||||
using std::vector<V>::rbegin;
|
||||
using std::vector<V>::rend;
|
||||
using std::vector<V>::cbegin;
|
||||
using std::vector<V>::cend;
|
||||
using std::vector<V>::crbegin;
|
||||
using std::vector<V>::crend;
|
||||
|
||||
using std::vector<V>::size;
|
||||
using std::vector<V>::max_size;
|
||||
using std::vector<V>::resize;
|
||||
using std::vector<V>::capacity;
|
||||
using std::vector<V>::empty;
|
||||
using std::vector<V>::reserve;
|
||||
using std::vector<V>::shrink_to_fit;
|
||||
|
||||
using std::vector<V>::front;
|
||||
using std::vector<V>::back;
|
||||
using std::vector<V>::data;
|
||||
|
||||
using std::vector<V>::assign;
|
||||
using std::vector<V>::push_back;
|
||||
using std::vector<V>::pop_back;
|
||||
using std::vector<V>::insert;
|
||||
using std::vector<V>::erase;
|
||||
using std::vector<V>::swap;
|
||||
using std::vector<V>::clear;
|
||||
using std::vector<V>::emplace;
|
||||
using std::vector<V>::emplace_back;
|
||||
using std::vector<V>::get_allocator;
|
||||
|
||||
|
||||
//Don't include operator[] and at() from std::vector,
|
||||
//since we redine them to take key_type instead of size_t
|
||||
reference operator[](const key_type id) {
|
||||
auto i = size_t(id);
|
||||
return std::vector<V>::operator[](i);
|
||||
}
|
||||
const_reference operator[](const key_type id) const {
|
||||
auto i = size_t(id);
|
||||
return std::vector<V>::operator[](i);
|
||||
}
|
||||
reference at(const key_type id) {
|
||||
auto i = size_t(id);
|
||||
return std::vector<V>::at(i);
|
||||
}
|
||||
const_reference at(const key_type id) const {
|
||||
auto i = size_t(id);
|
||||
return std::vector<V>::at(i);
|
||||
}
|
||||
|
||||
//Returns a range containing the keys
|
||||
key_range keys() const {
|
||||
return vtr::make_range(key_begin(), key_end());
|
||||
}
|
||||
public:
|
||||
|
||||
//Iterator class which is convertable to the key_type
|
||||
//This allows end-users to call the parent class's keys() member
|
||||
//to iterate through the keys with a range-based for loop
|
||||
class key_iterator : public std::iterator<std::bidirectional_iterator_tag, key_type> {
|
||||
public:
|
||||
//We use the intermediate type my_iter to avoid a potential ambiguity for which
|
||||
//clang generates errors and warnings
|
||||
using my_iter = typename std::iterator<std::bidirectional_iterator_tag, K>;
|
||||
using typename my_iter::value_type;
|
||||
using typename my_iter::iterator;
|
||||
using typename my_iter::pointer;
|
||||
using typename my_iter::reference;
|
||||
|
||||
key_iterator(key_iterator::value_type init): value_(init) {}
|
||||
|
||||
//vtr::vector assumes that the key time is convertable to size_t and
|
||||
//that all the underlying IDs are zero-based and contiguous. That means
|
||||
//we can just increment the underlying Id to build the next key.
|
||||
key_iterator operator++() {
|
||||
value_ = value_type(size_t(value_) + 1);
|
||||
return *this;
|
||||
}
|
||||
key_iterator operator--() {
|
||||
value_ = value_type(size_t(value_) - 1);
|
||||
return *this;
|
||||
}
|
||||
reference operator*() { return value_; }
|
||||
pointer operator->() { return &value_; }
|
||||
|
||||
friend bool operator== (const key_iterator lhs, const key_iterator rhs) { return lhs.value_ == rhs.value_; }
|
||||
friend bool operator!= (const key_iterator lhs, const key_iterator rhs) { return !(lhs == rhs); }
|
||||
|
||||
private:
|
||||
value_type value_;
|
||||
};
|
||||
|
||||
private:
|
||||
key_iterator key_begin() const { return key_iterator(key_type(0)); }
|
||||
key_iterator key_end() const { return key_iterator(key_type(size())); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
} //namespace
|
||||
#endif
|
|
@ -0,0 +1,144 @@
|
|||
#ifndef VTR_VECTOR_MAP
|
||||
#define VTR_VECTOR_MAP
|
||||
#include <vector>
|
||||
|
||||
#include "vtr_assert.h"
|
||||
#include "vtr_sentinels.h"
|
||||
|
||||
namespace vtr {
|
||||
|
||||
//A vector-like container which is indexed by K (instead of size_t as in std::vector).
|
||||
//
|
||||
//The main use of this container is to behave like a std::vector which is indexed by
|
||||
//vtr::StrongId.
|
||||
//
|
||||
//Requires that K be convertable to size_t with the size_t operator (i.e. size_t()), and
|
||||
//that the conversion results in a linearly increasing index into the underlying vector.
|
||||
//
|
||||
//This results in a container that is somewhat similar to a std::map (i.e. converts from one
|
||||
//type to another), but requires contiguously ascending (i.e. linear) keys. Unlike std::map
|
||||
//only the values are stored (at the specified index/key), reducing memory usage and improving
|
||||
//cache locality. Furthermore, operator[] and find() return the value or iterator directly
|
||||
//associated with the value (like std::vector) rather than a std::pair (like std::map).
|
||||
//insert() takes both the key and value as separate arguments and has no return value.
|
||||
//
|
||||
//Additionally, vector_map will silently create values for 'gaps' in the index range (i.e.
|
||||
//those elements are initialized with Sentinel::INVALID()).
|
||||
//
|
||||
//If you need a fully featured std::map like container without the above differences see
|
||||
//vtr::linear_map.
|
||||
//
|
||||
//If you do not need std::map-like features see vtr::vector.
|
||||
//
|
||||
//Note that it is possible to use vector_map with sparse/non-contiguous keys, but this is typically
|
||||
//memory inefficient as the underlying vector will allocate space for [0..size_t(max_key)-1],
|
||||
//where max_key is the largest key that has been inserted.
|
||||
//
|
||||
//As with a std::vector, it is the caller's responsibility to ensure there is sufficient space
|
||||
//when a given index/key before it is accessed. The exception to this are the find(), insert() and
|
||||
//update() methods which handle non-existing keys gracefully.
|
||||
template<typename K, typename V, typename Sentinel=DefaultSentinel<V>>
|
||||
class vector_map {
|
||||
public: //Public types
|
||||
typedef typename std::vector<V>::const_reference const_reference;
|
||||
typedef typename std::vector<V>::reference reference;
|
||||
|
||||
typedef typename std::vector<V>::iterator iterator;
|
||||
typedef typename std::vector<V>::const_iterator const_iterator;
|
||||
typedef typename std::vector<V>::const_reverse_iterator const_reverse_iterator;
|
||||
|
||||
public: //Constructor
|
||||
template<typename... Args>
|
||||
vector_map(Args&&... args)
|
||||
: vec_(std::forward<Args>(args)...)
|
||||
{ }
|
||||
|
||||
public: //Accessors
|
||||
|
||||
//Iterators
|
||||
const_iterator begin() const { return vec_.begin(); }
|
||||
const_iterator end() const { return vec_.end(); }
|
||||
const_reverse_iterator rbegin() const { return vec_.rbegin(); }
|
||||
const_reverse_iterator rend() const { return vec_.rend(); }
|
||||
|
||||
//Indexing
|
||||
const_reference operator[] (const K n) const {
|
||||
size_t index = size_t(n);
|
||||
VTR_ASSERT_SAFE_MSG(index >= 0 && index < vec_.size(), "Out-of-range index");
|
||||
return vec_[index];
|
||||
}
|
||||
|
||||
const_iterator find(const K key) const {
|
||||
if(size_t(key) < vec_.size()) {
|
||||
return vec_.begin() + size_t(key);
|
||||
} else {
|
||||
return vec_.end();
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t size() const { return vec_.size(); }
|
||||
|
||||
bool empty() const { return vec_.empty(); }
|
||||
|
||||
bool contains(const K key) const { return size_t(key) < vec_.size(); }
|
||||
size_t count(const K key) const { return contains(key) ? 1 : 0; }
|
||||
|
||||
public: //Mutators
|
||||
|
||||
//Delegate potentially overloaded functions to the underlying vector with perfect
|
||||
//forwarding
|
||||
template<typename... Args>
|
||||
void push_back(Args&&... args) { vec_.push_back(std::forward<Args>(args)...); }
|
||||
|
||||
template<typename... Args>
|
||||
void emplace_back(Args&&... args) { vec_.emplace_back(std::forward<Args>(args)...); }
|
||||
|
||||
template<typename... Args>
|
||||
void resize(Args&&... args) { vec_.resize(std::forward<Args>(args)...); }
|
||||
|
||||
void clear() { vec_.clear(); }
|
||||
|
||||
size_t capacity() const { return vec_.capacity(); }
|
||||
void shrink_to_fit() { vec_.shrink_to_fit(); }
|
||||
|
||||
//Iterators
|
||||
iterator begin() { return vec_.begin(); }
|
||||
iterator end() { return vec_.end(); }
|
||||
|
||||
//Indexing
|
||||
reference operator[] (const K n) {
|
||||
VTR_ASSERT_SAFE_MSG(size_t(n) < vec_.size(), "Out-of-range index");
|
||||
return vec_[size_t(n)];
|
||||
}
|
||||
|
||||
iterator find(const K key) {
|
||||
if(size_t(key) < vec_.size()) {
|
||||
return vec_.begin() + size_t(key);
|
||||
} else {
|
||||
return vec_.end();
|
||||
}
|
||||
}
|
||||
|
||||
void insert(const K key, const V value) {
|
||||
if(size_t(key) >= vec_.size()) {
|
||||
//Resize so key is in range
|
||||
vec_.resize(size_t(key) + 1, Sentinel::INVALID());
|
||||
}
|
||||
|
||||
//Insert the value
|
||||
operator[](key) = value;
|
||||
}
|
||||
|
||||
void update(const K key, const V value) { insert(key, value); }
|
||||
|
||||
//Swap (this enables std::swap via ADL)
|
||||
friend void swap(vector_map<K,V>& x, vector_map<K,V>& y) {
|
||||
std::swap(x.vec_, y.vec_);
|
||||
}
|
||||
private:
|
||||
std::vector<V> vec_;
|
||||
};
|
||||
|
||||
|
||||
} //namespace
|
||||
#endif
|
|
@ -0,0 +1,18 @@
|
|||
#include "vtr_version.h"
|
||||
|
||||
//This file is automatically processed by CMAKE and replaces
|
||||
//the values between ampersand's with the releveant CMAKE variable
|
||||
//before being compiled.
|
||||
namespace vtr {
|
||||
const char* VERSION = "@VTR_VERSION@";
|
||||
const char* VERSION_SHORT = "@VTR_VERSION_SHORT@";
|
||||
|
||||
const size_t VERSION_MAJOR = @VTR_VERSION_MAJOR@;
|
||||
const size_t VERSION_MINOR = @VTR_VERSION_MINOR@;
|
||||
const size_t VERSION_PATCH = @VTR_VERSION_PATCH@;
|
||||
const char* VERSION_PRERELEASE = "@VTR_VERSION_PRERELEASE@";
|
||||
|
||||
const char* VCS_REVISION = "@VTR_VCS_REVISION@";
|
||||
const char* COMPILER = "@VTR_COMPILER_INFO@";
|
||||
const char* BUILD_TIMESTAMP = "@VTR_BUILD_TIMESTAMP@";
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef VTR_VERSION_H
|
||||
#define VTR_VERSION_H
|
||||
#include <cstddef>
|
||||
|
||||
namespace vtr {
|
||||
extern const char* VERSION;
|
||||
extern const char* VERSION_SHORT;
|
||||
|
||||
extern const size_t VERSION_MAJOR;
|
||||
extern const size_t VERSION_MINOR;
|
||||
extern const size_t VERSION_PATCH;
|
||||
|
||||
extern const char* VCS_REVISION;
|
||||
extern const char* COMPILER;
|
||||
extern const char* BUILD_TIMESTAMP;
|
||||
extern const char* PRERELEASE_TAG;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -12,6 +12,8 @@ endif()
|
|||
project("vpr7_x2p" C CXX)
|
||||
|
||||
# idenify if we need graphics
|
||||
set(ENABLE_VPR_GRAPHIC_CXX_FLAG true)
|
||||
message(STATUS "Checking VPR graphics option ${ENABLE_VPR_GRAPHICS}")
|
||||
if (ENABLE_VPR_GRAPHICS)
|
||||
# check for dependencies
|
||||
message(STATUS "VPR graphics is turned on, searching for dependencies")
|
||||
|
@ -20,11 +22,13 @@ if (ENABLE_VPR_GRAPHICS)
|
|||
if (NOT X11_FOUND)
|
||||
message(WARNING "Failed to find required X11 library (on debian/ubuntu try 'sudo apt-get install libx11-dev' to install)")
|
||||
#Disable
|
||||
set(ENABLE_VPR_GRAPHICS false)
|
||||
set(ENABLE_VPR_GRAPHIC_CXX_FLAG false)
|
||||
endif()
|
||||
else ()
|
||||
set(ENABLE_VPR_GRAPHIC_CXX_FLAG false)
|
||||
endif()
|
||||
|
||||
if (NOT ENABLE_VPR_GRAPHICS)
|
||||
if (NOT ENABLE_VPR_GRAPHIC_CXX_FLAG)
|
||||
# Add a flag to notify compiler not to consider graphic-related source codes
|
||||
set (DISABLE_GRAPHIC_FLAGS "-DNO_GRAPHICS")
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DISABLE_GRAPHIC_FLAGS}")
|
||||
|
@ -56,14 +60,16 @@ target_include_directories(libvpr PUBLIC ${LIB_INCLUDE_DIRS})
|
|||
set_target_properties(libvpr PROPERTIES PREFIX "") #Avoid extra 'lib' prefix#Create the executable
|
||||
|
||||
#Specify link-time dependancies
|
||||
if (ENABLE_VPR_GRAPHICS)
|
||||
if (ENABLE_VPR_GRAPHIC_CXX_FLAG)
|
||||
target_link_libraries(libvpr
|
||||
libarchfpga
|
||||
X11
|
||||
libvtrutil
|
||||
readline)
|
||||
else ()
|
||||
target_link_libraries(libvpr
|
||||
libarchfpga
|
||||
libvtrutil
|
||||
readline)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
#include "arch_types.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
/*******************************************************************************
|
||||
* Global data types and constants
|
||||
|
@ -946,6 +947,7 @@ struct s_rr_node {
|
|||
short yhigh;
|
||||
|
||||
short ptc_num;
|
||||
std::vector<short> track_ids; /* Tileable arch support: Track indices in each GSB */
|
||||
|
||||
short cost_index;
|
||||
short occ;
|
||||
|
|
|
@ -0,0 +1,268 @@
|
|||
/**********************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018 LNIS - The University of Utah
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
***********************************************************************/
|
||||
|
||||
/************************************************************************
|
||||
* Filename: chan_node_details.cpp
|
||||
* Created by: Xifan Tang
|
||||
* Change history:
|
||||
* +-------------------------------------+
|
||||
* | Date | Author | Notes
|
||||
* +-------------------------------------+
|
||||
* | 2019/06/14 | Xifan Tang | Created
|
||||
* +-------------------------------------+
|
||||
***********************************************************************/
|
||||
/************************************************************************
|
||||
* This file contains member functions for class ChanNodeDetails
|
||||
***********************************************************************/
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include "chan_node_details.h"
|
||||
|
||||
/************************************************************************
|
||||
* Constructors
|
||||
***********************************************************************/
|
||||
ChanNodeDetails::ChanNodeDetails(const ChanNodeDetails& src) {
|
||||
/* duplicate */
|
||||
size_t chan_width = src.get_chan_width();
|
||||
this->reserve(chan_width);
|
||||
for (size_t itrack = 0; itrack < chan_width; ++itrack) {
|
||||
track_node_ids_.push_back(src.get_track_node_id(itrack));
|
||||
track_direction_.push_back(src.get_track_direction(itrack));
|
||||
seg_length_.push_back(src.get_track_segment_length(itrack));
|
||||
track_start_.push_back(src.is_track_start(itrack));
|
||||
track_end_.push_back(src.is_track_end(itrack));
|
||||
}
|
||||
}
|
||||
|
||||
ChanNodeDetails::ChanNodeDetails() {
|
||||
this->clear();
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Accessors
|
||||
***********************************************************************/
|
||||
size_t ChanNodeDetails::get_chan_width() const {
|
||||
assert(validate_chan_width());
|
||||
return track_node_ids_.size();
|
||||
}
|
||||
|
||||
size_t ChanNodeDetails::get_track_node_id(size_t track_id) const {
|
||||
assert(validate_track_id(track_id));
|
||||
return track_node_ids_[track_id];
|
||||
}
|
||||
|
||||
e_direction ChanNodeDetails::get_track_direction(size_t track_id) const {
|
||||
assert(validate_track_id(track_id));
|
||||
return track_direction_[track_id];
|
||||
}
|
||||
|
||||
size_t ChanNodeDetails::get_track_segment_length(size_t track_id) const {
|
||||
assert(validate_track_id(track_id));
|
||||
return seg_length_[track_id];
|
||||
}
|
||||
|
||||
bool ChanNodeDetails::is_track_start(size_t track_id) const {
|
||||
assert(validate_track_id(track_id));
|
||||
return track_start_[track_id];
|
||||
}
|
||||
|
||||
bool ChanNodeDetails::is_track_end(size_t track_id) const {
|
||||
assert(validate_track_id(track_id));
|
||||
return track_end_[track_id];
|
||||
}
|
||||
|
||||
/* Track_id is the starting point of group (whose is_start should be true)
|
||||
* This function will try to find the track_ids with the same directionality as track_id and seg_length
|
||||
* A group size is the number of such nodes between the starting points (include the 1st starting point)
|
||||
*/
|
||||
std::vector<size_t> ChanNodeDetails::get_seg_group(size_t track_id) const {
|
||||
assert(validate_chan_width());
|
||||
assert(validate_track_id(track_id));
|
||||
assert(is_track_start(track_id));
|
||||
|
||||
std::vector<size_t> group;
|
||||
/* Make sure a clean start */
|
||||
group.clear();
|
||||
|
||||
/* track_id is the first element */
|
||||
group.push_back(track_id);
|
||||
|
||||
for (size_t itrack = track_id; itrack < get_chan_width(); ++itrack) {
|
||||
if ( (get_track_direction(itrack) == get_track_direction(track_id) )
|
||||
&& (get_track_segment_length(itrack) == get_track_segment_length(track_id)) ) {
|
||||
if ( (false == is_track_start(itrack))
|
||||
|| ( (true == is_track_start(itrack)) && (itrack == track_id)) ) {
|
||||
group.push_back(itrack);
|
||||
continue;
|
||||
}
|
||||
/* Stop if this another starting point */
|
||||
if (true == is_track_start(itrack)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
/* Get a list of track_ids with the given list of track indices */
|
||||
std::vector<size_t> ChanNodeDetails::get_seg_group_node_id(std::vector<size_t> seg_group) const {
|
||||
std::vector<size_t> group;
|
||||
/* Make sure a clean start */
|
||||
group.clear();
|
||||
|
||||
for (size_t id = 0; id < seg_group.size(); ++id) {
|
||||
assert(validate_track_id(seg_group[id]));
|
||||
group.push_back(get_track_node_id(seg_group[id]));
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
/* Get the number of tracks that starts in this routing channel */
|
||||
size_t ChanNodeDetails::get_num_starting_tracks() const {
|
||||
size_t counter = 0;
|
||||
for (size_t itrack = 0; itrack < get_chan_width(); ++itrack) {
|
||||
if (false == is_track_start(itrack)) {
|
||||
continue;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
/* Get the number of tracks that ends in this routing channel */
|
||||
size_t ChanNodeDetails::get_num_ending_tracks() const {
|
||||
size_t counter = 0;
|
||||
for (size_t itrack = 0; itrack < get_chan_width(); ++itrack) {
|
||||
if (false == is_track_end(itrack)) {
|
||||
continue;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Mutators
|
||||
***********************************************************************/
|
||||
/* Reserve the capacitcy of vectors */
|
||||
void ChanNodeDetails::reserve(size_t chan_width) {
|
||||
track_node_ids_.reserve(chan_width);
|
||||
track_direction_.reserve(chan_width);
|
||||
seg_length_.reserve(chan_width);
|
||||
track_start_.reserve(chan_width);
|
||||
track_end_.reserve(chan_width);
|
||||
}
|
||||
|
||||
/* Add a track to the channel */
|
||||
void ChanNodeDetails::add_track(size_t track_node_id, e_direction track_direction, size_t seg_length, size_t is_start, size_t is_end) {
|
||||
track_node_ids_.push_back(track_node_id);
|
||||
track_direction_.push_back(track_direction);
|
||||
seg_length_.push_back(seg_length);
|
||||
track_start_.push_back(is_start);
|
||||
track_end_.push_back(is_end);
|
||||
}
|
||||
|
||||
/* Set tracks with a given direction to start */
|
||||
void ChanNodeDetails::set_tracks_start(e_direction track_direction) {
|
||||
for (size_t inode = 0; inode < get_chan_width(); ++inode) {
|
||||
/* Bypass non-match tracks */
|
||||
if (track_direction != get_track_direction(inode)) {
|
||||
}
|
||||
track_start_[inode] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set tracks with a given direction to end */
|
||||
void ChanNodeDetails::set_tracks_end(e_direction track_direction) {
|
||||
for (size_t inode = 0; inode < get_chan_width(); ++inode) {
|
||||
/* Bypass non-match tracks */
|
||||
if (track_direction != get_track_direction(inode)) {
|
||||
}
|
||||
track_end_[inode] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* rotate the track_node_id by an offset */
|
||||
void ChanNodeDetails::rotate_track_node_id(size_t offset, bool counter_rotate) {
|
||||
/* Rotate the node_ids by groups
|
||||
* A group begins from a track_start and ends before another track_start
|
||||
*/
|
||||
assert(validate_chan_width());
|
||||
for (size_t itrack = 0; itrack < get_chan_width(); ++itrack) {
|
||||
/* Bypass non-start segment */
|
||||
if (false == is_track_start(itrack) ) {
|
||||
continue;
|
||||
}
|
||||
/* Find the group nodes */
|
||||
std::vector<size_t> track_group = get_seg_group(itrack);
|
||||
/* Build a vector of the node ids of the tracks */
|
||||
std::vector<size_t> track_group_node_id = get_seg_group_node_id(track_group);
|
||||
/* Rotate or Counter rotate */
|
||||
if (true == counter_rotate) {
|
||||
std::rotate(track_group_node_id.begin(), track_group_node_id.end() - offset, track_group_node_id.end());
|
||||
} else {
|
||||
std::rotate(track_group_node_id.begin(), track_group_node_id.begin() + offset, track_group_node_id.end());
|
||||
}
|
||||
/* Update the node_ids */
|
||||
for (size_t inode = 0; inode < track_group.size(); ++inode) {
|
||||
track_node_ids_[track_group[inode]] = track_group_node_id[inode];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void ChanNodeDetails::clear() {
|
||||
track_node_ids_.clear();
|
||||
track_direction_.clear();
|
||||
seg_length_.clear();
|
||||
track_start_.clear();
|
||||
track_end_.clear();
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Validators
|
||||
***********************************************************************/
|
||||
bool ChanNodeDetails::validate_chan_width() const {
|
||||
size_t chan_width = track_node_ids_.size();
|
||||
if ( (chan_width == track_direction_.size())
|
||||
&&(chan_width == seg_length_.size())
|
||||
&&(chan_width == track_start_.size())
|
||||
&&(chan_width == track_end_.size()) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ChanNodeDetails::validate_track_id(size_t track_id) const {
|
||||
if ( (track_id < track_node_ids_.size())
|
||||
&& (track_id < track_direction_.size())
|
||||
&& (track_id < seg_length_.size())
|
||||
&& (track_id < track_start_.size())
|
||||
&& (track_id < track_end_.size()) ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/**********************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018 LNIS - The University of Utah
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
***********************************************************************/
|
||||
|
||||
/************************************************************************
|
||||
* Filename: chan_node_details.h
|
||||
* Created by: Xifan Tang
|
||||
* Change history:
|
||||
* +-------------------------------------+
|
||||
* | Date | Author | Notes
|
||||
* +-------------------------------------+
|
||||
* | 2019/06/11 | Xifan Tang | Created
|
||||
* +-------------------------------------+
|
||||
***********************************************************************/
|
||||
/************************************************************************
|
||||
* This file contains a class to model the details of routing node
|
||||
* in a channel:
|
||||
* 1. segment information: length, frequency etc.
|
||||
* 2. starting point of segment
|
||||
* 3. ending point of segment
|
||||
* 4. potentail track_id(ptc_num) of each segment
|
||||
***********************************************************************/
|
||||
|
||||
/* IMPORTANT:
|
||||
* The following preprocessing flags are added to
|
||||
* avoid compilation error when this headers are included in more than 1 times
|
||||
*/
|
||||
#ifndef CHAN_NODE_DETAILS_H
|
||||
#define CHAN_NODE_DETAILS_H
|
||||
|
||||
/*
|
||||
* Notes in include header files in a head file
|
||||
* Only include the neccessary header files
|
||||
* that is required by the data types in the function/class declarations!
|
||||
*/
|
||||
/* Header files should be included in a sequence */
|
||||
/* Standard header files required go first */
|
||||
#include <vector>
|
||||
#include "vpr_types.h"
|
||||
|
||||
/************************************************************************
|
||||
* ChanNodeDetails records segment length, directionality and starting of routing tracks
|
||||
* +---------------------------------+
|
||||
* | Index | Direction | Start Point |
|
||||
* +---------------------------------+
|
||||
* | 0 | --------> | Yes |
|
||||
* +---------------------------------+
|
||||
***********************************************************************/
|
||||
|
||||
|
||||
class ChanNodeDetails {
|
||||
public : /* Constructor */
|
||||
ChanNodeDetails(const ChanNodeDetails&); /* Duplication */
|
||||
ChanNodeDetails(); /* Initilization */
|
||||
public: /* Accessors */
|
||||
size_t get_chan_width() const;
|
||||
size_t get_track_node_id(size_t track_id) const;
|
||||
e_direction get_track_direction(size_t track_id) const;
|
||||
size_t get_track_segment_length(size_t track_id) const;
|
||||
bool is_track_start(size_t track_id) const;
|
||||
bool is_track_end(size_t track_id) const;
|
||||
std::vector<size_t> get_seg_group(size_t track_id) const;
|
||||
std::vector<size_t> get_seg_group_node_id(std::vector<size_t> seg_group) const;
|
||||
size_t get_num_starting_tracks() const;
|
||||
size_t get_num_ending_tracks() const;
|
||||
public: /* Mutators */
|
||||
void reserve(size_t chan_width); /* Reserve the capacitcy of vectors */
|
||||
void add_track(size_t track_node_id, e_direction track_direction, size_t seg_length, size_t is_start, size_t is_end);
|
||||
void set_tracks_start(e_direction track_direction);
|
||||
void set_tracks_end(e_direction track_direction);
|
||||
void rotate_track_node_id(size_t offset, bool counter_rotate); /* rotate the track_node_id by an offset */
|
||||
void clear();
|
||||
private: /* validators */
|
||||
bool validate_chan_width() const;
|
||||
bool validate_track_id(size_t track_id) const;
|
||||
private: /* Internal data */
|
||||
std::vector<size_t> track_node_ids_; /* indices of each track */
|
||||
std::vector<e_direction> track_direction_; /* direction of each track */
|
||||
std::vector<size_t> seg_length_; /* Length of each segment */
|
||||
std::vector<bool> track_start_; /* flag to identify if this is the starting point of the track */
|
||||
std::vector<bool> track_end_; /* flag to identify if this is the ending point of the track */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/**********************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018 LNIS - The University of Utah
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
***********************************************************************/
|
||||
|
||||
/************************************************************************
|
||||
* Filename: rr_graph_gsb.c
|
||||
* Created by: Xifan Tang
|
||||
* Change history:
|
||||
* +-------------------------------------+
|
||||
* | Date | Author | Notes
|
||||
* +-------------------------------------+
|
||||
* | 2019/06/12 | Xifan Tang | Created
|
||||
* +-------------------------------------+
|
||||
***********************************************************************/
|
||||
/************************************************************************
|
||||
* This file constains the member functions for class GSBConn
|
||||
************************************************************************/
|
||||
|
||||
#include "gsb_graph.h"
|
||||
#include "vtr_vector_map.h"
|
||||
|
||||
/************************************************************************
|
||||
* Constructors for class GSBGraph
|
||||
************************************************************************/
|
||||
/* Duplicate a object */
|
||||
GSBGraph::GSBGraph(const GSBGraph& gsb_graph) {
|
||||
|
||||
}
|
||||
|
||||
GSBGraph::GSBGraph() {
|
||||
coordinator_.clear();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Aggregators for class GSBGraph
|
||||
************************************************************************/
|
||||
/* Accessors: Aggregators */
|
||||
GSBGraph::node_range GSBGraph::nodes() const {
|
||||
return vtr::make_range(node_ids_.begin(), node_ids_.end());
|
||||
}
|
||||
|
||||
GSBGraph::edge_range GSBGraph::edges() const {
|
||||
return vtr::make_range(edge_ids_.begin(), edge_ids_.end());
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
/**********************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018 LNIS - The University of Utah
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
***********************************************************************/
|
||||
|
||||
/************************************************************************
|
||||
* Filename: gsb_graph.h
|
||||
* Created by: Xifan Tang
|
||||
* Change history:
|
||||
* +-------------------------------------+
|
||||
* | Date | Author | Notes
|
||||
* +-------------------------------------+
|
||||
* | 2019/06/12 | Xifan Tang | Created
|
||||
* +-------------------------------------+
|
||||
***********************************************************************/
|
||||
/************************************************************************
|
||||
* This file constains a class to model the connection of a
|
||||
* General Switch Block (GSB), which is a unified block of Connection Blocks
|
||||
* and Switch Blocks.
|
||||
* This block contains
|
||||
* 1. A switch block
|
||||
* 2. A X-direction Connection block locates at the left side of the switch block
|
||||
* 2. A Y-direction Connection block locates at the top side of the switch block
|
||||
*
|
||||
* +---------------------------------+
|
||||
* | Y-direction CB |
|
||||
* | [x][y + 1] |
|
||||
* +---------------------------------+
|
||||
*
|
||||
* TOP SIDE
|
||||
* +-------------+ +---------------------------------+
|
||||
* | | | OPIN_NODE CHAN_NODES OPIN_NODES |
|
||||
* | | | |
|
||||
* | | | OPIN_NODES OPIN_NODES |
|
||||
* | X-direction | | |
|
||||
* | CB | LEFT SIDE | Switch Block | RIGHT SIDE
|
||||
* | [x][y] | | [x][y] |
|
||||
* | | | |
|
||||
* | | | CHAN_NODES CHAN_NODES |
|
||||
* | | | |
|
||||
* | | | OPIN_NODES OPIN_NODES |
|
||||
* | | | |
|
||||
* | | | OPIN_NODE CHAN_NODES OPIN_NODES |
|
||||
* +-------------+ +---------------------------------+
|
||||
* BOTTOM SIDE
|
||||
*
|
||||
***********************************************************************/
|
||||
|
||||
/* IMPORTANT:
|
||||
* The following preprocessing flags are added to
|
||||
* avoid compilation error when this headers are included in more than 1 times
|
||||
*/
|
||||
#ifndef GSB_GRAPH_H
|
||||
#define GSB_GRAPH_H
|
||||
|
||||
/*
|
||||
* Notes in include header files in a head file
|
||||
* Only include the neccessary header files
|
||||
* that is required by the data types in the function/class declarations!
|
||||
*/
|
||||
/* Header files should be included in a sequence */
|
||||
/* Standard header files required go first */
|
||||
#include <vector>
|
||||
|
||||
/* External library header files */
|
||||
#include "vtr_vector.h"
|
||||
#include "vtr_range.h"
|
||||
|
||||
#include "device_coordinator.h"
|
||||
#include "vpr_types.h"
|
||||
#include "rr_graph_fwd.h"
|
||||
|
||||
/* Define open nodes */
|
||||
#define OPEN_NODE_ID RRNodeId(-1)
|
||||
#define OPEN_EDGE_ID RREdgeId(-1)
|
||||
|
||||
/***********************************************************************
|
||||
* This data structure focuses on modeling the internal pin-to-pin connections.
|
||||
* It is basically a collection of nodes and edges.
|
||||
* To make the data structure general, the nodes and edges are not linked to any another data
|
||||
* structures.
|
||||
*
|
||||
* node_ids_: a collection of nodes (basically ids) modelling routing tracks
|
||||
* which locate at each side of the GSB <0..num_nodes_per_side-1>
|
||||
*
|
||||
* node_directions_: Indicate if this node is an input or an output of the GSB
|
||||
* <0..num_nodes_per_side-1>
|
||||
*
|
||||
* node_types_: specify the types of the node, CHANX|CHANY|IPIN|OPIN
|
||||
*
|
||||
* node_sides_: specify the sides of the node on a GSB, TOP|RIGHT|BOTTOM|LEFT
|
||||
*
|
||||
* node_grid_sides_: specify the side of the node on which side of a GRID
|
||||
* for CHANX and CHANY, it is an invalid value
|
||||
* <0..num_nodes_per_side-1>
|
||||
*
|
||||
* node_in_edges_: indcies of input edges of a node
|
||||
* <0..num_nodes><0..num_input_edgess-1>
|
||||
*
|
||||
* node_out_edges_: indcies of output edges of a node
|
||||
* <0..num_nodes><0..num_output_edges-1>
|
||||
*
|
||||
* edge_ids_: a collection of indices of edges, <0..num_edges-1>, which connects the nodes
|
||||
*
|
||||
* edge_src_nodes_: indcies of input nodes of an edge (driving nodes for each edge)
|
||||
* <0..num_input_nodes-1>
|
||||
*
|
||||
* edge_sink_nodes_: indices of output nodes of an edge (fan-out nodes for each edge)
|
||||
* <0..num_output_nodes-1>
|
||||
*
|
||||
***********************************************************************/
|
||||
|
||||
class GSBGraph {
|
||||
public: /* Types */
|
||||
typedef vtr::vector<RRNodeId, RRNodeId>::const_iterator node_iterator;
|
||||
typedef vtr::vector<RREdgeId, RREdgeId>::const_iterator edge_iterator;
|
||||
typedef vtr::Range<node_iterator> node_range;
|
||||
typedef vtr::Range<edge_iterator> edge_range;
|
||||
public: /* Constructors */
|
||||
GSBGraph(const GSBGraph&); /* A constructor to duplicate */
|
||||
GSBGraph();
|
||||
public: /* Accessors */
|
||||
/* Aggregates */
|
||||
node_range nodes() const;
|
||||
edge_range edges() const;
|
||||
private: /* Internal Data */
|
||||
/* Coordinator of this GSB */
|
||||
DeviceCoordinator coordinator_;
|
||||
|
||||
/* nodes on each side */
|
||||
vtr::vector<RRNodeId, RRNodeId> node_ids_;
|
||||
vtr::vector<RRNodeId, t_rr_type> node_types_;
|
||||
vtr::vector<RRNodeId, enum e_side> node_sides_;
|
||||
vtr::vector<RRNodeId, enum e_direction> node_directions_;
|
||||
vtr::vector<RRNodeId, enum e_side> node_grid_sides_;
|
||||
|
||||
vtr::vector<RRNodeId, std::vector<RREdgeId>> node_in_edges;
|
||||
vtr::vector<RRNodeId, std::vector<RREdgeId>> node_out_edges;
|
||||
|
||||
/* edges */
|
||||
vtr::vector<RREdgeId, RREdgeId> edge_ids_;
|
||||
vtr::vector<RREdgeId, RREdgeId> edge_src_nodes_; /* each element is a node_id */
|
||||
vtr::vector<RREdgeId, RREdgeId> edge_sink_nodes_; /* each element is a node_id */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef RR_GRAPH_OBJ_FWD_H
|
||||
#define RR_GRAPH_OBJ_FWD_H
|
||||
|
||||
#include "vtr_strong_id.h"
|
||||
|
||||
class RRGraph;
|
||||
|
||||
struct rr_node_id_tag;
|
||||
struct rr_edge_id_tag;
|
||||
struct rr_switch_id_tag;
|
||||
struct rr_segment_id_tag;
|
||||
|
||||
typedef vtr::StrongId<rr_node_id_tag> RRNodeId;
|
||||
typedef vtr::StrongId<rr_edge_id_tag> RREdgeId;
|
||||
typedef vtr::StrongId<rr_switch_id_tag, short> RRSwitchId;
|
||||
typedef vtr::StrongId<rr_segment_id_tag, short> RRSegmentId;
|
||||
|
||||
#endif
|
|
@ -44,6 +44,7 @@
|
|||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include "vpr_types.h"
|
||||
#include "globals.h"
|
||||
#include "vpr_utils.h"
|
||||
|
@ -54,6 +55,391 @@
|
|||
#include "fpga_x2p_types.h"
|
||||
#include "rr_graph_tileable_builder.h"
|
||||
|
||||
#include "chan_node_details.h"
|
||||
#include "device_coordinator.h"
|
||||
|
||||
/************************************************************************
|
||||
* Local function in the file
|
||||
***********************************************************************/
|
||||
|
||||
/************************************************************************
|
||||
* Generate the number of tracks for each types of routing segments
|
||||
* w.r.t. the frequency of each of segments and channel width
|
||||
* Note that if we dertermine the number of tracks per type using
|
||||
* chan_width * segment_frequency / total_freq may cause
|
||||
* The total track num may not match the chan_width,
|
||||
* therefore, we assign tracks one by one until we meet the frequency requirement
|
||||
* In this way, we can assign the number of tracks with repect to frequency
|
||||
***********************************************************************/
|
||||
static
|
||||
std::vector<size_t> get_num_tracks_per_seg_type(size_t chan_width,
|
||||
std::vector<t_segment_inf> segment_inf,
|
||||
bool use_full_seg_groups) {
|
||||
std::vector<size_t> result;
|
||||
std::vector<double> demand;
|
||||
/* Make sure a clean start */
|
||||
result.resize(segment_inf.size());
|
||||
demand.resize(segment_inf.size());
|
||||
|
||||
/* Scale factor so we can divide by any length
|
||||
* and still use integers */
|
||||
/* Get the sum of frequency */
|
||||
size_t scale = 1;
|
||||
size_t freq_sum = 0;
|
||||
for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) {
|
||||
scale *= segment_inf[iseg].length;
|
||||
freq_sum += segment_inf[iseg].frequency;
|
||||
}
|
||||
size_t reduce = scale * freq_sum;
|
||||
|
||||
/* Init assignments to 0 and set the demand values */
|
||||
/* Get the fraction of each segment type considering the frequency:
|
||||
* num_track_per_seg = chan_width * (freq_of_seg / sum_freq)
|
||||
*/
|
||||
for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) {
|
||||
result[iseg] = 0;
|
||||
demand[iseg] = scale * chan_width * segment_inf[iseg].frequency;
|
||||
if (true == use_full_seg_groups) {
|
||||
demand[iseg] /= segment_inf[iseg].length;
|
||||
}
|
||||
}
|
||||
|
||||
/* check if the sum of num_tracks, matches the chan_width */
|
||||
/* Keep assigning tracks until we use them up */
|
||||
size_t assigned = 0;
|
||||
size_t size = 0;
|
||||
size_t imax = 0;
|
||||
while (assigned < chan_width) {
|
||||
/* Find current maximum demand */
|
||||
double max = 0;
|
||||
for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) {
|
||||
if (demand[iseg] > max) {
|
||||
imax = iseg;
|
||||
}
|
||||
max = std::max(demand[iseg], max);
|
||||
}
|
||||
|
||||
/* Assign tracks to the type and reduce the types demand */
|
||||
size = (use_full_seg_groups ? segment_inf[imax].length : 1);
|
||||
demand[imax] -= reduce;
|
||||
result[imax] += size;
|
||||
assigned += size;
|
||||
}
|
||||
|
||||
/* Undo last assignment if we were closer to goal without it */
|
||||
if ((assigned - chan_width) > (size / 2)) {
|
||||
result[imax] -= size;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Build details of routing tracks in a channel
|
||||
* The function will
|
||||
* 1. Assign the segments for each routing channel,
|
||||
* To be specific, for each routing track, we assign a routing segment.
|
||||
* The assignment is subject to users' specifications, such as
|
||||
* a. length of each type of segment
|
||||
* b. frequency of each type of segment.
|
||||
* c. routing channel width
|
||||
*
|
||||
* 2. The starting point of each segment in the channel will be assigned
|
||||
* For each segment group with same directionality (tracks have the same length),
|
||||
* every L track will be a starting point (where L denotes the length of segments)
|
||||
* In this case, if the number of tracks is not a multiple of L,
|
||||
* indeed we may have some <L segments. This can be considered as a side effect.
|
||||
* But still the rr_graph is tileable, which is the first concern!
|
||||
*
|
||||
* Here is a quick example of Length-4 wires in a W=12 routing channel
|
||||
* +---------------------------------+
|
||||
* | Index | Direction | Start Point |
|
||||
* +---------------------------------+
|
||||
* | 0 | --------> | Yes |
|
||||
* +---------------------------------+
|
||||
* | 1 | <-------- | Yes |
|
||||
* +---------------------------------+
|
||||
* | 2 | --------> | No |
|
||||
* +---------------------------------+
|
||||
* | 3 | <-------- | No |
|
||||
* +---------------------------------+
|
||||
* | 4 | --------> | No |
|
||||
* +---------------------------------+
|
||||
* | 5 | <-------- | No |
|
||||
* +---------------------------------+
|
||||
* | 7 | --------> | No |
|
||||
* +---------------------------------+
|
||||
* | 8 | <-------- | No |
|
||||
* +---------------------------------+
|
||||
* | 9 | --------> | Yes |
|
||||
* +---------------------------------+
|
||||
* | 10 | <-------- | Yes |
|
||||
* +---------------------------------+
|
||||
* | 11 | --------> | No |
|
||||
* +---------------------------------+
|
||||
* | 12 | <-------- | No |
|
||||
* +---------------------------------+
|
||||
*
|
||||
* 3. SPECIAL for fringes: TOP|RIGHT|BOTTOM|RIGHT
|
||||
* if device_side is NUM_SIDES, we assume this channel does not locate on borders
|
||||
* All segments will start and ends with no exception
|
||||
*
|
||||
* 4. IMPORTANT: we should be aware that channel width maybe different
|
||||
* in X-direction and Y-direction channels!!!
|
||||
* So we will load segment details for different channels
|
||||
***********************************************************************/
|
||||
static
|
||||
ChanNodeDetails build_unidir_chan_node_details(size_t chan_width, size_t max_seg_length,
|
||||
enum e_side device_side,
|
||||
std::vector<t_segment_inf> segment_inf) {
|
||||
ChanNodeDetails chan_node_details;
|
||||
/* Correct the chan_width: it should be an even number */
|
||||
if (0 != chan_width % 2) {
|
||||
chan_width++; /* increment it to be even */
|
||||
}
|
||||
assert (0 == chan_width % 2);
|
||||
|
||||
/* Reserve channel width */
|
||||
chan_node_details.reserve(chan_width);
|
||||
/* Return if zero width is forced */
|
||||
if (0 == chan_width) {
|
||||
return chan_node_details;
|
||||
}
|
||||
|
||||
/* Find the number of segments required by each group */
|
||||
std::vector<size_t> num_tracks = get_num_tracks_per_seg_type(chan_width/2, segment_inf, TRUE);
|
||||
|
||||
/* Add node to ChanNodeDetails */
|
||||
size_t cur_track = 0;
|
||||
for (size_t iseg = 0; iseg < segment_inf.size(); ++iseg) {
|
||||
/* segment length will be set to maxium segment length if this is a longwire */
|
||||
size_t seg_len = segment_inf[iseg].length;
|
||||
if (TRUE == segment_inf[iseg].longline) {
|
||||
seg_len = max_seg_length;
|
||||
}
|
||||
for (size_t itrack = 0; itrack < num_tracks[iseg]; ++itrack) {
|
||||
bool seg_start = false;
|
||||
/* Every length of wire, we set a starting point */
|
||||
if (0 == itrack % seg_len) {
|
||||
seg_start = true;
|
||||
}
|
||||
/* Since this is a unidirectional routing architecture,
|
||||
* Add a pair of tracks, 1 INC_DIRECTION track and 1 DEC_DIRECTION track
|
||||
*/
|
||||
chan_node_details.add_track(cur_track, INC_DIRECTION, seg_len, seg_start, false);
|
||||
cur_track++;
|
||||
chan_node_details.add_track(cur_track, DEC_DIRECTION, seg_len, seg_start, false);
|
||||
cur_track++;
|
||||
}
|
||||
}
|
||||
/* Check if all the tracks have been satisified */
|
||||
assert (cur_track == chan_width);
|
||||
|
||||
/* If this is on the border of a device, segments should start */
|
||||
switch (device_side) {
|
||||
case TOP:
|
||||
case RIGHT:
|
||||
/* INC_DIRECTION should all end */
|
||||
chan_node_details.set_tracks_end(INC_DIRECTION);
|
||||
/* DEC_DIRECTION should all start */
|
||||
chan_node_details.set_tracks_start(DEC_DIRECTION);
|
||||
break;
|
||||
case BOTTOM:
|
||||
case LEFT:
|
||||
/* INC_DIRECTION should all start */
|
||||
chan_node_details.set_tracks_start(INC_DIRECTION);
|
||||
/* DEC_DIRECTION should all end */
|
||||
chan_node_details.set_tracks_end(DEC_DIRECTION);
|
||||
break;
|
||||
case NUM_SIDES:
|
||||
break;
|
||||
default:
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"(File:%s, [LINE%d]) Invalid device_side!\n",
|
||||
__FILE__, __LINE__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return chan_node_details;
|
||||
}
|
||||
|
||||
/* Deteremine the side of a io grid */
|
||||
static
|
||||
enum e_side determine_io_grid_pin_side(const DeviceCoordinator& device_size,
|
||||
const DeviceCoordinator& grid_coordinator) {
|
||||
/* TOP side IO of FPGA */
|
||||
if (device_size.get_y() == grid_coordinator.get_y()) {
|
||||
return BOTTOM; /* Such I/O has only Bottom side pins */
|
||||
} else if (device_size.get_x() == grid_coordinator.get_x()) { /* RIGHT side IO of FPGA */
|
||||
return LEFT; /* Such I/O has only Left side pins */
|
||||
} else if (0 == grid_coordinator.get_y()) { /* BOTTOM side IO of FPGA */
|
||||
return TOP; /* Such I/O has only Top side pins */
|
||||
} else if (0 == grid_coordinator.get_x()) { /* LEFT side IO of FPGA */
|
||||
return RIGHT; /* Such I/O has only Right side pins */
|
||||
} else {
|
||||
vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])I/O Grid is in the center part of FPGA! Currently unsupported!\n",
|
||||
__FILE__, __LINE__);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Get a list of pin_index for a grid (either OPIN or IPIN)
|
||||
* For IO_TYPE, only one side will be used, we consider one side of pins
|
||||
* For others, we consider all the sides
|
||||
***********************************************************************/
|
||||
static
|
||||
std::vector<int> get_grid_side_pins(const t_grid_tile& cur_grid, enum e_pin_type pin_type, enum e_side pin_side, int pin_height) {
|
||||
std::vector<int> pin_list;
|
||||
/* Make sure a clear start */
|
||||
pin_list.clear();
|
||||
|
||||
for (int ipin = 0; ipin < cur_grid.type->num_pins; ++ipin) {
|
||||
if ( (1 == cur_grid.type->pinloc[pin_height][pin_side][ipin])
|
||||
&& (pin_type == cur_grid.type->pin_class[ipin]) ) {
|
||||
pin_list.push_back(ipin);
|
||||
}
|
||||
}
|
||||
return pin_list;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Get the number of pins for a grid (either OPIN or IPIN)
|
||||
* For IO_TYPE, only one side will be used, we consider one side of pins
|
||||
* For others, we consider all the sides
|
||||
***********************************************************************/
|
||||
static
|
||||
size_t get_grid_num_pins(const t_grid_tile& cur_grid, enum e_pin_type pin_type, enum e_side io_side) {
|
||||
size_t num_pins = 0;
|
||||
Side io_side_manager(io_side);
|
||||
/* For IO_TYPE sides */
|
||||
for (size_t side = 0; side < NUM_SIDES; ++side) {
|
||||
Side side_manager(side);
|
||||
/* skip unwanted sides */
|
||||
if ( (IO_TYPE == cur_grid.type)
|
||||
&& (side != io_side_manager.to_size_t()) ) {
|
||||
continue;
|
||||
}
|
||||
/* Get pin list */
|
||||
for (int height = 0; height < cur_grid.type->height; ++height) {
|
||||
std::vector<int> pin_list = get_grid_side_pins(cur_grid, pin_type, side_manager.get_side(), height);
|
||||
num_pins += pin_list.size();
|
||||
}
|
||||
}
|
||||
|
||||
return num_pins;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Estimate the number of rr_nodes per category:
|
||||
* CHANX, CHANY, IPIN, OPIN, SOURCE, SINK
|
||||
***********************************************************************/
|
||||
static
|
||||
std::vector<size_t> estimate_num_rr_nodes_per_type(const DeviceCoordinator& device_size,
|
||||
std::vector<std::vector<t_grid_tile>> grids,
|
||||
std::vector<size_t> chan_width,
|
||||
std::vector<t_segment_inf> segment_infs) {
|
||||
std::vector<size_t> num_rr_nodes_per_type;
|
||||
/* reserve the vector:
|
||||
* we have the follow type:
|
||||
* SOURCE = 0, SINK, IPIN, OPIN, CHANX, CHANY, INTRA_CLUSTER_EDGE, NUM_RR_TYPES
|
||||
* NUM_RR_TYPES and INTRA_CLUSTER_EDGE will be 0
|
||||
*/
|
||||
num_rr_nodes_per_type.resize(NUM_RR_TYPES);
|
||||
/* Make sure a clean start */
|
||||
for (size_t i = 0; i < NUM_RR_TYPES; ++i) {
|
||||
num_rr_nodes_per_type[i] = 0;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* 1. Search the grid and find the number OPINs and IPINs per grid
|
||||
* Note that the number of SOURCE nodes are the same as OPINs
|
||||
* and the number of SINK nodes are the same as IPINs
|
||||
***********************************************************************/
|
||||
for (size_t ix = 0; ix < grids.size(); ++ix) {
|
||||
for (size_t iy = 0; iy < grids[ix].size(); ++iy) {
|
||||
/* Skip EMPTY tiles */
|
||||
if (EMPTY_TYPE == grids[ix][iy].type) {
|
||||
continue;
|
||||
}
|
||||
/* Skip height>1 tiles (mostly heterogeneous blocks) */
|
||||
if (0 < grids[ix][iy].offset) {
|
||||
continue;
|
||||
}
|
||||
enum e_side io_side = NUM_SIDES;
|
||||
/* If this is the block on borders, we consider IO side */
|
||||
if (IO_TYPE == grid[ix][iy].type) {
|
||||
DeviceCoordinator io_device_size(device_size.get_x() - 1, device_size.get_y() - 1);
|
||||
DeviceCoordinator grid_coordinator(ix, iy);
|
||||
io_side = determine_io_grid_pin_side(device_size, grid_coordinator);
|
||||
}
|
||||
/* get the number of OPINs */
|
||||
num_rr_nodes_per_type[OPIN] += get_grid_num_pins(grids[ix][iy], DRIVER, io_side);
|
||||
/* get the number of IPINs */
|
||||
num_rr_nodes_per_type[IPIN] += get_grid_num_pins(grids[ix][iy], RECEIVER, io_side);
|
||||
}
|
||||
}
|
||||
/* SOURCE and SINK */
|
||||
num_rr_nodes_per_type[SOURCE] = num_rr_nodes_per_type[OPIN];
|
||||
num_rr_nodes_per_type[SINK] = num_rr_nodes_per_type[IPIN];
|
||||
|
||||
/************************************************************************
|
||||
* 2. Assign the segments for each routing channel,
|
||||
* To be specific, for each routing track, we assign a routing segment.
|
||||
* The assignment is subject to users' specifications, such as
|
||||
* a. length of each type of segment
|
||||
* b. frequency of each type of segment.
|
||||
* c. routing channel width
|
||||
*
|
||||
* SPECIAL for fringes:
|
||||
* All segments will start and ends with no exception
|
||||
*
|
||||
* IMPORTANT: we should be aware that channel width maybe different
|
||||
* in X-direction and Y-direction channels!!!
|
||||
* So we will load segment details for different channels
|
||||
***********************************************************************/
|
||||
/* For X-direction Channel */
|
||||
/* For LEFT side of FPGA */
|
||||
ChanNodeDetails left_chanx_details = build_unidir_chan_node_details(chan_width[0], device_size.get_x() - 2, LEFT, segment_infs);
|
||||
for (size_t iy = 0; iy < device_size.get_y() - 1; ++iy) {
|
||||
num_rr_nodes_per_type[CHANX] += left_chanx_details.get_num_starting_tracks();
|
||||
}
|
||||
/* For RIGHT side of FPGA */
|
||||
ChanNodeDetails right_chanx_details = build_unidir_chan_node_details(chan_width[0], device_size.get_x() - 2, RIGHT, segment_infs);
|
||||
for (size_t iy = 0; iy < device_size.get_y() - 1; ++iy) {
|
||||
num_rr_nodes_per_type[CHANX] += right_chanx_details.get_num_starting_tracks();
|
||||
}
|
||||
/* For core of FPGA */
|
||||
ChanNodeDetails core_chanx_details = build_unidir_chan_node_details(chan_width[1], device_size.get_x() - 2, NUM_SIDES, segment_infs);
|
||||
for (size_t ix = 1; ix < grids.size() - 2; ++ix) {
|
||||
for (size_t iy = 1; iy < grids[ix].size() - 2; ++iy) {
|
||||
num_rr_nodes_per_type[CHANX] += core_chanx_details.get_num_starting_tracks();
|
||||
}
|
||||
}
|
||||
|
||||
/* For Y-direction Channel */
|
||||
/* For TOP side of FPGA */
|
||||
ChanNodeDetails top_chany_details = build_unidir_chan_node_details(chan_width[1], device_size.get_y() - 2, TOP, segment_infs);
|
||||
for (size_t ix = 0; ix < device_size.get_x() - 1; ++ix) {
|
||||
num_rr_nodes_per_type[CHANY] += top_chany_details.get_num_starting_tracks();
|
||||
}
|
||||
/* For BOTTOM side of FPGA */
|
||||
ChanNodeDetails bottom_chany_details = build_unidir_chan_node_details(chan_width[1], device_size.get_y() - 2, BOTTOM, segment_infs);
|
||||
for (size_t ix = 0; ix < device_size.get_x() - 1; ++ix) {
|
||||
num_rr_nodes_per_type[CHANY] += bottom_chany_details.get_num_starting_tracks();
|
||||
}
|
||||
/* For core of FPGA */
|
||||
ChanNodeDetails core_chany_details = build_unidir_chan_node_details(chan_width[1], device_size.get_y() - 2, NUM_SIDES, segment_infs);
|
||||
for (size_t ix = 1; ix < grids.size() - 2; ++ix) {
|
||||
for (size_t iy = 1; iy < grids[ix].size() - 2; ++iy) {
|
||||
num_rr_nodes_per_type[CHANY] += core_chany_details.get_num_starting_tracks();
|
||||
}
|
||||
}
|
||||
|
||||
return num_rr_nodes_per_type;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* Main function of this file
|
||||
* Builder for a detailed uni-directional tileable rr_graph
|
||||
|
@ -115,22 +501,55 @@ t_rr_graph build_tileable_unidir_rr_graph(INP int L_num_types,
|
|||
/* Reset warning flag */
|
||||
*Warnings = RR_GRAPH_NO_WARN;
|
||||
|
||||
/* Create a matrix of grid */
|
||||
DeviceCoordinator device_size(L_nx + 2, L_ny + 2);
|
||||
std::vector< std::vector<t_grid_tile> > grids;
|
||||
/* reserve vector capacity to be memory efficient */
|
||||
grids.resize(L_nx + 2);
|
||||
for (int ix = 0; ix < (L_nx + 2); ++ix) {
|
||||
grids[ix].resize(L_ny + 2);
|
||||
for (int iy = 0; ix < (L_ny + 2); ++iy) {
|
||||
grid[ix][iy] = L_grid[ix][iy];
|
||||
}
|
||||
}
|
||||
/* Create a vector of channel width, we support X-direction and Y-direction has different W */
|
||||
std::vector<size_t> device_chan_width;
|
||||
device_chan_width.push_back(chan_width);
|
||||
device_chan_width.push_back(chan_width);
|
||||
|
||||
/* Create a vector of segment_inf */
|
||||
std::vector<t_segment_inf> segment_infs;
|
||||
for (int iseg = 0; iseg < num_seg_types; ++iseg) {
|
||||
segment_infs.push_back(segment_inf[iseg]);
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* 1. Assign the segments for each routing channel,
|
||||
* To be specific, for each routing track, we assign a routing segment.
|
||||
* The assignment is subject to users' specifications, such as
|
||||
* a. length of each type of segment
|
||||
* b. frequency of each type of segment.
|
||||
* c. routing channel width
|
||||
* 2. Estimate the number of nodes in the rr_graph
|
||||
* This will estimate the number of
|
||||
* a. IPINs, input pins of each grid
|
||||
* b. OPINs, output pins of each grid
|
||||
* c. SOURCE, virtual node which drives OPINs
|
||||
* d. SINK, virtual node which is connected to IPINs
|
||||
* e. CHANX and CHANY, routing segments of each channel
|
||||
***********************************************************************/
|
||||
std::vector<size_t> num_rr_nodes_per_type = estimate_num_rr_nodes_per_type(device_size, grids, device_chan_width, segment_infs);
|
||||
|
||||
/************************************************************************
|
||||
* 3. Allocate the rr_nodes
|
||||
***********************************************************************/
|
||||
rr_graph.num_rr_nodes = 0;
|
||||
for (size_t i = 0; i < num_rr_nodes_per_type.size(); ++i) {
|
||||
rr_graph.num_rr_nodes += num_rr_nodes_per_type[i];
|
||||
}
|
||||
/* use calloc to initialize everything to be zero */
|
||||
rr_graph.rr_node = (t_rr_node*)my_calloc(rr_graph.num_rr_nodes, sizeof(t_rr_node));
|
||||
|
||||
/************************************************************************
|
||||
* 4. Initialize the basic information of rr_nodes:
|
||||
* coordinators: xlow, ylow, xhigh, yhigh,
|
||||
* features: capacity, track_ids, ptc_num, direction
|
||||
* grid_info : pb_graph_pin
|
||||
***********************************************************************/
|
||||
/* Check the channel width */
|
||||
int nodes_per_chan = chan_width;
|
||||
assert(chan_width > 0);
|
||||
t_seg_details *seg_details = NULL;
|
||||
seg_details = alloc_and_load_seg_details(&nodes_per_chan,
|
||||
std::max(L_nx, L_ny),
|
||||
num_seg_types, segment_inf,
|
||||
TRUE, FALSE, UNI_DIRECTIONAL);
|
||||
|
||||
/************************************************************************
|
||||
* 3. Create the connectivity of OPINs
|
||||
|
@ -140,7 +559,7 @@ t_rr_graph build_tileable_unidir_rr_graph(INP int L_num_types,
|
|||
int **Fc_in = NULL; /* [0..num_types-1][0..num_pins-1] */
|
||||
boolean Fc_clipped;
|
||||
Fc_clipped = FALSE;
|
||||
Fc_in = alloc_and_load_actual_fc(L_num_types, types, nodes_per_chan,
|
||||
Fc_in = alloc_and_load_actual_fc(L_num_types, types, chan_width,
|
||||
FALSE, UNI_DIRECTIONAL, &Fc_clipped, ignore_Fc_0);
|
||||
if (Fc_clipped) {
|
||||
*Warnings |= RR_GRAPH_WARN_FC_CLIPPED;
|
||||
|
@ -153,7 +572,7 @@ t_rr_graph build_tileable_unidir_rr_graph(INP int L_num_types,
|
|||
***********************************************************************/
|
||||
int **Fc_out = NULL; /* [0..num_types-1][0..num_pins-1] */
|
||||
Fc_clipped = FALSE;
|
||||
Fc_out = alloc_and_load_actual_fc(L_num_types, types, nodes_per_chan,
|
||||
Fc_out = alloc_and_load_actual_fc(L_num_types, types, chan_width,
|
||||
TRUE, UNI_DIRECTIONAL, &Fc_clipped, ignore_Fc_0);
|
||||
|
||||
/************************************************************************
|
||||
|
@ -164,6 +583,7 @@ t_rr_graph build_tileable_unidir_rr_graph(INP int L_num_types,
|
|||
* c. handle direct-connections
|
||||
***********************************************************************/
|
||||
/* Alloc node lookups, count nodes, alloc rr nodes */
|
||||
/*
|
||||
rr_graph.num_rr_nodes = 0;
|
||||
rr_graph.rr_node_indices = alloc_and_load_rr_node_indices(nodes_per_chan, L_nx, L_ny,
|
||||
&(rr_graph.num_rr_nodes), seg_details);
|
||||
|
@ -171,6 +591,7 @@ t_rr_graph build_tileable_unidir_rr_graph(INP int L_num_types,
|
|||
memset(rr_node, 0, sizeof(t_rr_node) * rr_graph.num_rr_nodes);
|
||||
boolean* L_rr_edge_done = (boolean *) my_malloc(sizeof(boolean) * rr_graph.num_rr_nodes);
|
||||
memset(L_rr_edge_done, 0, sizeof(boolean) * rr_graph.num_rr_nodes);
|
||||
*/
|
||||
|
||||
/* handle direct-connections */
|
||||
t_clb_to_clb_directs* clb_to_clb_directs = NULL;
|
||||
|
@ -183,7 +604,7 @@ t_rr_graph build_tileable_unidir_rr_graph(INP int L_num_types,
|
|||
* a. cost_index
|
||||
* b. RC tree
|
||||
***********************************************************************/
|
||||
rr_graph_externals(timing_inf, segment_inf, num_seg_types, nodes_per_chan,
|
||||
rr_graph_externals(timing_inf, segment_inf, num_seg_types, chan_width,
|
||||
wire_to_ipin_switch, base_cost_type);
|
||||
|
||||
return rr_graph;
|
||||
|
|
|
@ -403,7 +403,7 @@ void alloc_and_load_rr_graph_route_structs(t_rr_graph* local_rr_graph) {
|
|||
|
||||
int inode;
|
||||
|
||||
local_rr_graph->rr_node_route_inf = (t_rr_node_route_inf *) my_malloc(local_rr_graph->num_rr_nodes * sizeof(t_rr_node_route_inf));
|
||||
local_rr_graph->rr_node_route_inf = (t_rr_node_route_inf *) my_calloc(local_rr_graph->num_rr_nodes, sizeof(t_rr_node_route_inf));
|
||||
|
||||
for (inode = 0; inode < local_rr_graph->num_rr_nodes; inode++) {
|
||||
local_rr_graph->rr_node_route_inf[inode].prev_node = NO_PREVIOUS;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include <cassert>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
|
@ -1345,55 +1345,71 @@ const char* RRGSB::gen_cb_verilog_routing_track_name(t_rr_type cb_type,
|
|||
std::string y_str = std::to_string(get_cb_y(cb_type));
|
||||
std::string track_id_str = std::to_string(track_id);
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << cb_name << "_" << x_str << "__" << y_str << "__midout_" << track_id_str << "_";
|
||||
std::string ret = oss.str();
|
||||
|
||||
return ret.c_str();
|
||||
char* ret = (char*)my_malloc(sizeof(char)*
|
||||
( cb_name.length() + 1
|
||||
+ x_str.length() + 2
|
||||
+ y_str.length() + 9
|
||||
+ track_id_str.length() + 1
|
||||
+ 1));
|
||||
sprintf (ret, "%s_%s__%s__midout_%s_",
|
||||
cb_name.c_str(), x_str.c_str(), y_str.c_str(), track_id_str.c_str());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char* RRGSB::gen_sb_verilog_module_name() const {
|
||||
std::string x_str = std::to_string(get_sb_x());
|
||||
std::string y_str = std::to_string(get_sb_y());
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "sb_" << x_str << "__" << y_str << "_" ;
|
||||
std::string ret = oss.str();
|
||||
char* ret = (char*)my_malloc(sizeof(char)*
|
||||
( 2 + 1
|
||||
+ x_str.length() + 2
|
||||
+ y_str.length() + 1
|
||||
+ 1));
|
||||
sprintf (ret, "sb_%s__%s_",
|
||||
x_str.c_str(), y_str.c_str());
|
||||
|
||||
return ret.c_str();
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char* RRGSB::gen_sb_verilog_instance_name() const {
|
||||
char* ret = (char*)my_malloc(sizeof(char)*
|
||||
( strlen(gen_sb_verilog_module_name()) + 3
|
||||
+ 1));
|
||||
sprintf (ret, "%s_0_",
|
||||
gen_sb_verilog_module_name());
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << gen_sb_verilog_module_name() << "_0_" ;
|
||||
std::string ret = oss.str();
|
||||
|
||||
return ret.c_str();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Public Accessors Verilog writer */
|
||||
const char* RRGSB::gen_sb_verilog_side_module_name(enum e_side side, size_t seg_id) const {
|
||||
Side side_manager(side);
|
||||
|
||||
std::string prefix_str(gen_sb_verilog_module_name());
|
||||
std::string seg_id_str(std::to_string(seg_id));
|
||||
std::string side_str(side_manager.to_string());
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << gen_sb_verilog_module_name() << "_" << side_str << "_seg_" << "_" << seg_id_str << "_" ;
|
||||
std::string ret = oss.str();
|
||||
char* ret = (char*)my_malloc(sizeof(char)*
|
||||
( prefix_str.length() + 1
|
||||
+ side_str.length() + 5
|
||||
+ seg_id_str.length() + 1
|
||||
+ 1));
|
||||
sprintf (ret, "%s_%s_seg_%s_",
|
||||
prefix_str.c_str(), side_str.c_str(), seg_id_str.c_str());
|
||||
|
||||
return ret.c_str();
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char* RRGSB::gen_sb_verilog_side_instance_name(enum e_side side, size_t seg_id) const {
|
||||
std::string prefix_str = gen_sb_verilog_side_module_name(side, seg_id);
|
||||
char* ret = (char*)my_malloc(sizeof(char)*
|
||||
( prefix_str.length() + 3
|
||||
+ 1));
|
||||
sprintf (ret, "%s_0_",
|
||||
prefix_str.c_str());
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << gen_sb_verilog_side_module_name(side, seg_id) << "_0_" ;
|
||||
std::string ret = oss.str();
|
||||
|
||||
return ret.c_str();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Public Accessors Verilog writer */
|
||||
|
@ -1405,23 +1421,29 @@ const char* RRGSB::gen_cb_verilog_module_name(t_rr_type cb_type) const {
|
|||
std::string x_str = std::to_string(get_cb_x(cb_type));
|
||||
std::string y_str = std::to_string(get_cb_y(cb_type));
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << prefix_str << "_" << x_str << "__" << y_str << "_" ;
|
||||
std::string ret = oss.str();
|
||||
char* ret = (char*)my_malloc(sizeof(char)*
|
||||
( prefix_str.length() + 1
|
||||
+ x_str.length() + 2
|
||||
+ y_str.length() + 1
|
||||
+ 1));
|
||||
sprintf (ret, "%s_%s__%s_",
|
||||
prefix_str.c_str(), x_str.c_str(), y_str.c_str());
|
||||
|
||||
return ret.c_str();
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char* RRGSB::gen_cb_verilog_instance_name(t_rr_type cb_type) const {
|
||||
/* check */
|
||||
assert (validate_cb_type(cb_type));
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << gen_cb_verilog_module_name(cb_type) << "_0_" ;
|
||||
std::string ret = oss.str();
|
||||
|
||||
return ret.c_str();
|
||||
std::string prefix_str = gen_cb_verilog_module_name(cb_type);
|
||||
char* ret = (char*)my_malloc(sizeof(char)*
|
||||
(prefix_str.length() + 3
|
||||
+ 1));
|
||||
sprintf (ret, "%s_0_",
|
||||
prefix_str.c_str());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Public mutators */
|
||||
|
|
|
@ -1,3 +1,38 @@
|
|||
/**********************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2018 LNIS - The University of Utah
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
***********************************************************************/
|
||||
|
||||
/************************************************************************
|
||||
* Filename: rr_blocks.h
|
||||
* Created by: Xifan Tang
|
||||
* Change history:
|
||||
* +-------------------------------------+
|
||||
* | Date | Author | Notes
|
||||
* +-------------------------------------+
|
||||
* | 2019/06/12 | Xifan Tang | Created
|
||||
* +-------------------------------------+
|
||||
***********************************************************************/
|
||||
|
||||
/* IMPORTANT:
|
||||
* The following preprocessing flags are added to
|
||||
* avoid compilation error when this headers are included in more than 1 times
|
||||
|
|
|
@ -10,10 +10,8 @@ char* convert_cb_type_to_string(t_rr_type chan_type) {
|
|||
switch(chan_type) {
|
||||
case CHANX:
|
||||
return "cbx";
|
||||
break;
|
||||
case CHANY:
|
||||
return "cby";
|
||||
break;
|
||||
default:
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"(File:%s, [LINE%d])Invalid type of channel!\n",
|
||||
|
@ -26,10 +24,8 @@ char* convert_chan_type_to_string(t_rr_type chan_type) {
|
|||
switch(chan_type) {
|
||||
case CHANX:
|
||||
return "chanx";
|
||||
break;
|
||||
case CHANY:
|
||||
return "chany";
|
||||
break;
|
||||
default:
|
||||
vpr_printf(TIO_MESSAGE_ERROR,
|
||||
"(File:%s, [LINE%d])Invalid type of channel!\n",
|
||||
|
@ -42,10 +38,8 @@ char* convert_chan_rr_node_direction_to_string(enum PORTS chan_rr_node_direction
|
|||
switch(chan_rr_node_direction) {
|
||||
case IN_PORT:
|
||||
return "in";
|
||||
break;
|
||||
case OUT_PORT:
|
||||
return "out";
|
||||
break;
|
||||
default:
|
||||
vpr_printf(TIO_MESSAGE_ERROR, "(File:%s, [LINE%d])Invalid type of port!\n", __FILE__, __LINE__);
|
||||
exit(1);
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
#include "fpga_x2p_pbtypes_utils.h"
|
||||
#include "fpga_x2p_globals.h"
|
||||
|
||||
#include "fpga_x2p_pb_rr_graph.h"
|
||||
|
||||
/* Count the number of rr_graph nodes that should be allocated
|
||||
* (a) INPUT pins at the top-level pb_graph_node should be a local_rr_node and plus a SOURCE
|
||||
* (b) CLOCK pins at the top-level pb_graph_node should be a local_rr_node and plus a SOURCE
|
||||
|
@ -104,6 +106,7 @@ void init_one_rr_node_pack_cost_for_phy_graph_node(INP t_pb_graph_pin* cur_pb_gr
|
|||
|
||||
|
||||
/* Override the fan-in and fan-out for a top/primitive pb_graph_node */
|
||||
static
|
||||
void override_one_rr_node_for_top_primitive_phy_pb_graph_node(INP t_pb_graph_pin* cur_pb_graph_pin,
|
||||
INOUTP t_rr_graph* local_rr_graph,
|
||||
int cur_rr_node_index,
|
||||
|
@ -151,6 +154,7 @@ void override_one_rr_node_for_top_primitive_phy_pb_graph_node(INP t_pb_graph_pin
|
|||
}
|
||||
|
||||
/* initialize a rr_node in a rr_graph of phyical pb_graph_node */
|
||||
static
|
||||
void init_one_rr_node_for_phy_pb_graph_node(INP t_pb_graph_pin* cur_pb_graph_pin,
|
||||
INOUTP t_rr_graph* local_rr_graph,
|
||||
int cur_rr_node_index,
|
||||
|
@ -206,7 +210,9 @@ void init_one_rr_node_for_phy_pb_graph_node(INP t_pb_graph_pin* cur_pb_graph_pin
|
|||
local_rr_graph->rr_node[cur_rr_node_index].prev_edge = OPEN;
|
||||
|
||||
local_rr_graph->rr_node[cur_rr_node_index].capacity = 1;
|
||||
local_rr_graph->rr_node[cur_rr_node_index].occ = 0;
|
||||
local_rr_graph->rr_node[cur_rr_node_index].type = rr_node_type;
|
||||
local_rr_graph->rr_node[cur_rr_node_index].cost_index = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -270,6 +276,7 @@ void connect_one_rr_node_for_phy_pb_graph_node(INP t_pb_graph_pin* cur_pb_graph_
|
|||
/* Recursively configure all the rr_nodes in the rr_graph
|
||||
* Initialize the routing cost, fan-in rr_nodes and fan-out rr_nodes, and switches
|
||||
*/
|
||||
static
|
||||
void rec_init_rr_graph_for_phy_pb_graph_node(INP t_pb_graph_node* cur_pb_graph_node,
|
||||
INOUTP t_rr_graph* local_rr_graph,
|
||||
int* cur_rr_node_index) {
|
||||
|
@ -418,6 +425,7 @@ void rec_init_rr_graph_for_phy_pb_graph_node(INP t_pb_graph_node* cur_pb_graph_n
|
|||
/* Recursively connect all the rr_nodes in the rr_graph
|
||||
* output_edges, output_switches
|
||||
*/
|
||||
static
|
||||
void rec_connect_rr_graph_for_phy_pb_graph_node(INP t_pb_graph_node* cur_pb_graph_node,
|
||||
INOUTP t_rr_graph* local_rr_graph,
|
||||
int* cur_rr_node_index) {
|
||||
|
@ -598,6 +606,7 @@ void alloc_and_load_rr_graph_for_phy_pb_graph_node(INP t_pb_graph_node* top_pb_g
|
|||
/* Check the vpack_net_num of a rr_node mapped to a pb_graph_pin and
|
||||
* mark the used vpack_net_num in the list
|
||||
*/
|
||||
static
|
||||
void mark_vpack_net_used_in_pb_pin(t_pb* cur_op_pb, t_pb_graph_pin* cur_pb_graph_pin,
|
||||
int L_num_vpack_nets, boolean* vpack_net_used_in_pb) {
|
||||
int inode;
|
||||
|
@ -621,6 +630,7 @@ void mark_vpack_net_used_in_pb_pin(t_pb* cur_op_pb, t_pb_graph_pin* cur_pb_graph
|
|||
/* Recursively visit all the child pbs and
|
||||
* mark the used vpack_net_num in the list
|
||||
*/
|
||||
static
|
||||
void mark_vpack_net_used_in_pb(t_pb* cur_op_pb,
|
||||
int L_num_vpack_nets, boolean* vpack_net_used_in_pb) {
|
||||
int mode_index, ipb, jpb;
|
||||
|
@ -718,6 +728,7 @@ void alloc_and_load_phy_pb_rr_graph_nets(INP t_pb* cur_op_pb,
|
|||
}
|
||||
|
||||
/* Find the rr_node in the primitive node of a pb_rr_graph*/
|
||||
static
|
||||
void sync_pb_graph_pin_vpack_net_num_to_phy_pb(t_rr_node* cur_op_pb_rr_graph,
|
||||
t_pb_graph_pin* cur_pb_graph_pin,
|
||||
t_rr_graph* local_rr_graph) {
|
||||
|
@ -787,6 +798,7 @@ void sync_pb_graph_pin_vpack_net_num_to_phy_pb(t_rr_node* cur_op_pb_rr_graph,
|
|||
return;
|
||||
}
|
||||
|
||||
static
|
||||
void rec_sync_wired_pb_vpack_net_num_to_phy_pb_rr_graph(t_pb_graph_node* cur_pb_graph_node,
|
||||
t_rr_node* op_pb_rr_graph,
|
||||
t_rr_graph* local_rr_graph) {
|
||||
|
@ -851,6 +863,7 @@ void rec_sync_wired_pb_vpack_net_num_to_phy_pb_rr_graph(t_pb_graph_node* cur_pb_
|
|||
* synchronize the vpack_net_num of the top-level/primitive pb_graph_pin
|
||||
* to the physical pb rr_node nodes
|
||||
*/
|
||||
static
|
||||
void rec_sync_pb_vpack_net_num_to_phy_pb_rr_graph(t_pb* cur_op_pb,
|
||||
t_rr_graph* local_rr_graph) {
|
||||
int mode_index, ipb, jpb;
|
||||
|
@ -921,6 +934,7 @@ void rec_sync_pb_vpack_net_num_to_phy_pb_rr_graph(t_pb* cur_op_pb,
|
|||
* 3. Find the SOURCE and SINK rr_nodes related to the pb_graph_pin
|
||||
* 4. Configure the net_rr_terminals with the SINK/SOURCE rr_nodes
|
||||
*/
|
||||
static
|
||||
void alloc_and_load_phy_pb_rr_graph_net_rr_terminals(INP t_pb* cur_op_pb,
|
||||
t_rr_graph* local_rr_graph) {
|
||||
int inet, inode, rr_node_net_name;
|
||||
|
@ -1013,10 +1027,11 @@ void alloc_and_load_phy_pb_rr_graph_net_rr_terminals(INP t_pb* cur_op_pb,
|
|||
return;
|
||||
}
|
||||
|
||||
static
|
||||
void alloc_pb_rr_graph_rr_indexed_data(t_rr_graph* local_rr_graph) {
|
||||
/* inside a cluster, I do not consider rr_indexed_data cost, set to 1 since other costs are multiplied by it */
|
||||
alloc_rr_graph_rr_indexed_data(local_rr_graph, 1);
|
||||
local_rr_graph->rr_indexed_data[0].base_cost = 1;
|
||||
local_rr_graph->rr_indexed_data[0].base_cost = 1.;
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1025,8 +1040,8 @@ void alloc_pb_rr_graph_rr_indexed_data(t_rr_graph* local_rr_graph) {
|
|||
* Add an output edge to the rr_node of the used input
|
||||
* connect it to the rr_node of the used LUT output
|
||||
*/
|
||||
static
|
||||
void add_rr_node_edge_to_one_wired_lut(t_pb_graph_node* cur_pb_graph_node,
|
||||
t_pb_type* cur_pb_type,
|
||||
t_rr_node* op_pb_rr_graph,
|
||||
t_rr_graph* local_rr_graph) {
|
||||
int iport, ipin;
|
||||
|
@ -1140,6 +1155,7 @@ void add_rr_node_edge_to_one_wired_lut(t_pb_graph_node* cur_pb_graph_node,
|
|||
/* Add rr edges connecting from an input of a LUT to its output
|
||||
* IMPORTANT: this is only applied to LUT which operates in wire mode (a buffer)
|
||||
*/
|
||||
static
|
||||
void rec_add_unused_rr_graph_wired_lut_rr_edges(INP t_pb_graph_node* cur_op_pb_graph_node,
|
||||
INP t_rr_node* cur_op_pb_rr_graph,
|
||||
INOUTP t_rr_graph* local_rr_graph) {
|
||||
|
@ -1162,7 +1178,6 @@ void rec_add_unused_rr_graph_wired_lut_rr_edges(INP t_pb_graph_node* cur_op_pb_g
|
|||
* connect it to the rr_node of the used LUT output
|
||||
*/
|
||||
add_rr_node_edge_to_one_wired_lut(cur_op_pb_graph_node,
|
||||
cur_pb_type,
|
||||
cur_op_pb_rr_graph,
|
||||
local_rr_graph);
|
||||
}
|
||||
|
@ -1194,6 +1209,7 @@ void rec_add_unused_rr_graph_wired_lut_rr_edges(INP t_pb_graph_node* cur_op_pb_g
|
|||
/* Add rr edges connecting from an input of a LUT to its output
|
||||
* IMPORTANT: this is only applied to LUT which operates in wire mode (a buffer)
|
||||
*/
|
||||
static
|
||||
void rec_add_rr_graph_wired_lut_rr_edges(INP t_pb* cur_op_pb,
|
||||
INOUTP t_rr_graph* local_rr_graph) {
|
||||
int mode_index, ipb, jpb, imode;
|
||||
|
@ -1214,7 +1230,6 @@ void rec_add_rr_graph_wired_lut_rr_edges(INP t_pb* cur_op_pb,
|
|||
* connect it to the rr_node of the used LUT output
|
||||
*/
|
||||
add_rr_node_edge_to_one_wired_lut(cur_op_pb->pb_graph_node,
|
||||
cur_pb_type,
|
||||
cur_op_pb->rr_graph,
|
||||
local_rr_graph);
|
||||
}
|
||||
|
@ -1253,6 +1268,7 @@ void rec_add_rr_graph_wired_lut_rr_edges(INP t_pb* cur_op_pb,
|
|||
* For each multiple-source net, I add a new source as the unique source in routing purpose
|
||||
* As so, edges have to be added to the decendents of sources
|
||||
*/
|
||||
static
|
||||
int add_virtual_sources_to_rr_graph_multi_sources(t_rr_graph* local_rr_graph) {
|
||||
int inet, isrc;
|
||||
int unique_src_node;
|
||||
|
@ -1271,6 +1287,8 @@ int add_virtual_sources_to_rr_graph_multi_sources(t_rr_graph* local_rr_graph) {
|
|||
unique_src_node = local_rr_graph->num_rr_nodes - 1;
|
||||
local_rr_graph->rr_node[unique_src_node].type = SOURCE;
|
||||
local_rr_graph->rr_node[unique_src_node].capacity = 1;
|
||||
local_rr_graph->rr_node[unique_src_node].occ = 0;
|
||||
local_rr_graph->rr_node[unique_src_node].cost_index = 0;
|
||||
local_rr_graph->rr_node[unique_src_node].fan_in = 0;
|
||||
local_rr_graph->rr_node[unique_src_node].num_drive_rr_nodes = 0;
|
||||
local_rr_graph->rr_node[unique_src_node].drive_rr_nodes = NULL;
|
||||
|
@ -1314,6 +1332,9 @@ void alloc_and_load_rr_graph_for_phy_pb(INP t_pb* cur_op_pb,
|
|||
/* Allocate rr_graph*/
|
||||
cur_phy_pb->rr_graph = (t_rr_graph*) my_calloc(1, sizeof(t_rr_graph));
|
||||
|
||||
/* Allocate and initialize cost index */
|
||||
alloc_pb_rr_graph_rr_indexed_data(cur_phy_pb->rr_graph);
|
||||
|
||||
/* Create rr_graph */
|
||||
alloc_and_load_rr_graph_for_phy_pb_graph_node(cur_phy_pb->pb_graph_node, cur_phy_pb->rr_graph);
|
||||
|
||||
|
@ -1332,8 +1353,6 @@ void alloc_and_load_rr_graph_for_phy_pb(INP t_pb* cur_op_pb,
|
|||
/* Allocate trace in rr_graph */
|
||||
alloc_rr_graph_route_static_structs(cur_phy_pb->rr_graph, nx * ny); /* TODO: nx * ny should be reduced for pb-only routing */
|
||||
|
||||
alloc_pb_rr_graph_rr_indexed_data(cur_phy_pb->rr_graph);
|
||||
|
||||
/* Fill the net_rr_terminals with
|
||||
* 1. pin-to-pin mapping in pb_graph_node in cur_op_pb
|
||||
* 2. rr_graph in the cur_op_pb
|
||||
|
|
|
@ -423,7 +423,6 @@ void dump_compact_verilog_one_physical_block(t_sram_orgz_info* cur_sram_orgz_inf
|
|||
0, temp_conf_bits_msb - 1);
|
||||
}
|
||||
|
||||
|
||||
/* Quote all the sub blocks*/
|
||||
for (iz = 0; iz < phy_block_type->capacity; iz++) {
|
||||
/* Local Vdd and Gnd, subckt name*/
|
||||
|
|
|
@ -464,16 +464,19 @@ void dump_verilog_membank_config_module(FILE* fp,
|
|||
case SPICE_MODEL_DESIGN_CMOS:
|
||||
assert( 0 == num_reserved_bl );
|
||||
assert( 0 == num_reserved_wl );
|
||||
|
||||
/* Declare normal BL / WL inputs */
|
||||
fprintf(fp, " output wire [%d:%d] %s%s, //---- Normal Bit lines \n",
|
||||
fprintf(fp, " output wire [%d:%d] %s%s",
|
||||
0, num_bl - 1, mem_model->prefix, top_netlist_normal_bl_port_postfix);
|
||||
fprintf(fp, ", //---- Normal Bit lines \n");
|
||||
|
||||
fprintf(fp, " output wire [%d:%d] %s%s",
|
||||
0, num_wl - 1, mem_model->prefix, top_netlist_normal_wl_port_postfix);
|
||||
/* Declare inverted wires if needed */
|
||||
if (1 == num_blb_ports) {
|
||||
fprintf(fp, ", //---- Normal Word lines \n");
|
||||
} else {
|
||||
fprintf(fp, " //---- Normal Word lines\n);\n");
|
||||
fprintf(fp, " //---- Normal Word lines\n");
|
||||
}
|
||||
if (1 == num_blb_ports) {
|
||||
fprintf(fp, " output wire [%d:%d] %s%s",
|
||||
|
@ -482,13 +485,13 @@ void dump_verilog_membank_config_module(FILE* fp,
|
|||
if (1 == num_wlb_ports) {
|
||||
fprintf(fp, ", //---- Inverted Normal Bit lines \n");
|
||||
} else {
|
||||
fprintf(fp, " //---- Inverted Normal Bit lines \n);\n");
|
||||
fprintf(fp, " //---- Inverted Normal Bit lines \n");
|
||||
}
|
||||
if (1 == num_wlb_ports) {
|
||||
fprintf(fp, " output wire [%d:%d] %s%s //---- Inverted Normal Word lines \n",
|
||||
0, num_wl - 1, mem_model->prefix, top_netlist_normal_wlb_port_postfix);
|
||||
fprintf(fp, ");\n");
|
||||
}
|
||||
fprintf(fp, ");\n");
|
||||
break;
|
||||
case SPICE_MODEL_DESIGN_RRAM:
|
||||
/* Check: there should be reserved BLs and WLs */
|
||||
|
|
|
@ -350,6 +350,51 @@ void stats_mux_verilog_model_pb_node_rec(t_llist** muxes_head,
|
|||
return;
|
||||
}
|
||||
|
||||
/* Print a port of pb_types,
|
||||
* SRAM ports are not printed here!!!
|
||||
* Important feature: manage the comma between ports
|
||||
* Make sure there is no redundant comma and there is no comma after the last element if specified
|
||||
*/
|
||||
static
|
||||
void dump_verilog_pb_type_one_bus_port(FILE* fp,
|
||||
t_pb_type* cur_pb_type,
|
||||
char* port_prefix,
|
||||
char* port_type_str,
|
||||
t_port* pb_type_port,
|
||||
boolean dump_port_type,
|
||||
boolean dump_explicit_port_map) {
|
||||
if (TRUE == dump_port_type) {
|
||||
fprintf(fp, "%s ", port_type_str);
|
||||
fprintf(fp, "[0:%d] %s__%s ",
|
||||
pb_type_port->num_pins - 1,
|
||||
port_prefix, pb_type_port->name);
|
||||
} else {
|
||||
if ((NULL != cur_pb_type->spice_model)
|
||||
&& (TRUE == dump_explicit_port_map)
|
||||
&& (TRUE == cur_pb_type->spice_model->dump_explicit_port_map)) {
|
||||
fprintf(fp, ".%s(",
|
||||
pb_type_port->spice_model_port->lib_name);
|
||||
}
|
||||
fprintf(fp, "{");
|
||||
for (int ipin = 0; ipin < pb_type_port->num_pins; ++ipin) {
|
||||
if (0 < ipin) {
|
||||
fprintf(fp, ", ");
|
||||
}
|
||||
fprintf(fp, "%s",
|
||||
gen_verilog_one_pb_type_pin_name(port_prefix, pb_type_port, ipin));
|
||||
}
|
||||
fprintf(fp, "}");
|
||||
if ((NULL != cur_pb_type->spice_model)
|
||||
&& (TRUE == dump_explicit_port_map)
|
||||
&& (TRUE == cur_pb_type->spice_model->dump_explicit_port_map)) {
|
||||
fprintf(fp, ")");
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Print ports of pb_types,
|
||||
* SRAM ports are not printed here!!!
|
||||
* Important feature: manage the comma between ports
|
||||
|
@ -360,7 +405,8 @@ void dump_verilog_pb_type_bus_ports(FILE* fp,
|
|||
int use_global_clock,
|
||||
t_pb_type* cur_pb_type,
|
||||
boolean dump_port_type,
|
||||
boolean dump_last_comma) {
|
||||
boolean dump_last_comma,
|
||||
boolean dump_explicit_port_map) {
|
||||
int iport;
|
||||
int num_pb_type_input_port = 0;
|
||||
t_port** pb_type_input_ports = NULL;
|
||||
|
@ -398,16 +444,9 @@ void dump_verilog_pb_type_bus_ports(FILE* fp,
|
|||
fprintf(fp, ", ");
|
||||
}
|
||||
}
|
||||
if (TRUE == dump_port_type) {
|
||||
fprintf(fp, "inout ");
|
||||
fprintf(fp, "[0:%d] %s__%s ",
|
||||
pb_type_inout_ports[iport]->num_pins - 1,
|
||||
formatted_port_prefix, pb_type_inout_ports[iport]->name);
|
||||
} else {
|
||||
fprintf(fp, "%s__%s[0:%d] ",
|
||||
formatted_port_prefix, pb_type_inout_ports[iport]->name,
|
||||
pb_type_inout_ports[iport]->num_pins - 1);
|
||||
}
|
||||
dump_verilog_pb_type_one_bus_port(fp, cur_pb_type, formatted_port_prefix, "inout",
|
||||
pb_type_inout_ports[iport], dump_port_type, dump_explicit_port_map);
|
||||
|
||||
/* Update the counter */
|
||||
num_dumped_port++;
|
||||
}
|
||||
|
@ -424,16 +463,8 @@ void dump_verilog_pb_type_bus_ports(FILE* fp,
|
|||
fprintf(fp, ", ");
|
||||
}
|
||||
}
|
||||
if (TRUE == dump_port_type) {
|
||||
fprintf(fp, "input ");
|
||||
fprintf(fp, " [0:%d] %s__%s ",
|
||||
pb_type_input_ports[iport]->num_pins - 1,
|
||||
formatted_port_prefix, pb_type_input_ports[iport]->name);
|
||||
} else {
|
||||
fprintf(fp, " %s__%s[0:%d] ",
|
||||
formatted_port_prefix, pb_type_input_ports[iport]->name,
|
||||
pb_type_input_ports[iport]->num_pins - 1);
|
||||
}
|
||||
dump_verilog_pb_type_one_bus_port(fp, cur_pb_type, formatted_port_prefix, "input",
|
||||
pb_type_input_ports[iport], dump_port_type, dump_explicit_port_map);
|
||||
/* Update the counter */
|
||||
num_dumped_port++;
|
||||
}
|
||||
|
@ -450,22 +481,15 @@ void dump_verilog_pb_type_bus_ports(FILE* fp,
|
|||
fprintf(fp, ", ");
|
||||
}
|
||||
}
|
||||
if (TRUE == dump_port_type) {
|
||||
fprintf(fp, "output ");
|
||||
fprintf(fp, " %s__%s[0:%d]",
|
||||
formatted_port_prefix, pb_type_output_ports[iport]->name,
|
||||
pb_type_output_ports[iport]->num_pins - 1);
|
||||
} else {
|
||||
fprintf(fp, " %s__%s[0:%d]",
|
||||
formatted_port_prefix, pb_type_output_ports[iport]->name,
|
||||
pb_type_output_ports[iport]->num_pins - 1);
|
||||
}
|
||||
dump_verilog_pb_type_one_bus_port(fp, cur_pb_type, formatted_port_prefix, "output",
|
||||
pb_type_output_ports[iport], dump_port_type, dump_explicit_port_map);
|
||||
/* Update the counter */
|
||||
num_dumped_port++;
|
||||
}
|
||||
|
||||
/* Clocks */
|
||||
/* Find pb_type clock ports */
|
||||
if (0 == use_global_clock) {
|
||||
pb_type_clk_ports = find_pb_type_ports_match_spice_model_port_type(cur_pb_type, SPICE_MODEL_PORT_CLOCK, &num_pb_type_clk_port);
|
||||
/* Print all the clk ports */
|
||||
for (iport = 0; iport < num_pb_type_clk_port; iport++) {
|
||||
|
@ -476,19 +500,12 @@ void dump_verilog_pb_type_bus_ports(FILE* fp,
|
|||
fprintf(fp, ", ");
|
||||
}
|
||||
}
|
||||
if (TRUE == dump_port_type) {
|
||||
fprintf(fp, "input");
|
||||
fprintf(fp, " %s__%s[0:%d]",
|
||||
formatted_port_prefix, pb_type_clk_ports[iport]->name,
|
||||
pb_type_output_ports[iport]->num_pins - 1);
|
||||
} else {
|
||||
fprintf(fp, " %s__%s[0:%d]",
|
||||
formatted_port_prefix, pb_type_clk_ports[iport]->name,
|
||||
pb_type_output_ports[iport]->num_pins - 1);
|
||||
}
|
||||
dump_verilog_pb_type_one_bus_port(fp, cur_pb_type, formatted_port_prefix, "input",
|
||||
pb_type_clk_ports[iport], dump_port_type, dump_explicit_port_map);
|
||||
/* Update the counter */
|
||||
num_dumped_port++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dump the last comma, when the option is enabled and there is something dumped */
|
||||
if ((0 < num_dumped_port)&&(TRUE == dump_last_comma)) {
|
||||
|
|
|
@ -23,7 +23,8 @@ void dump_verilog_pb_type_bus_ports(FILE* fp,
|
|||
int use_global_clock,
|
||||
t_pb_type* cur_pb_type,
|
||||
boolean dump_port_type,
|
||||
boolean dump_last_comma);
|
||||
boolean dump_last_comma,
|
||||
boolean dump_explicit_port_map);
|
||||
|
||||
void dump_verilog_pb_type_ports(FILE* fp,
|
||||
char* port_prefix,
|
||||
|
|
|
@ -235,7 +235,7 @@ void dump_verilog_pb_generic_primitive(t_sram_orgz_info* cur_sram_orgz_info,
|
|||
/* assert */
|
||||
num_sram = count_num_sram_bits_one_spice_model(verilog_model, -1);
|
||||
/* print ports --> input ports */
|
||||
dump_verilog_pb_type_ports(fp, port_prefix, 0, prim_pb_type, FALSE, FALSE, TRUE);
|
||||
dump_verilog_pb_type_bus_ports(fp, port_prefix, 1, prim_pb_type, FALSE, FALSE, verilog_model->dump_explicit_port_map);
|
||||
|
||||
/* IOPADs requires a specical port to output */
|
||||
if (SPICE_MODEL_IOPAD == verilog_model->type) {
|
||||
|
@ -573,32 +573,6 @@ void dump_verilog_pb_primitive_lut(t_sram_orgz_info* cur_sram_orgz_info,
|
|||
fprintf(fp, ");\n");
|
||||
/* Definition ends*/
|
||||
|
||||
/* Specify inputs are wires */
|
||||
pb_type_input_ports = find_pb_type_ports_match_spice_model_port_type(cur_pb_type, SPICE_MODEL_PORT_INPUT, &num_pb_type_input_port);
|
||||
assert(1 == num_pb_type_input_port);
|
||||
fprintf(fp, "wire [0:%d] %s__%s;\n",
|
||||
input_ports[0]->size - 1, port_prefix, pb_type_input_ports[0]->name);
|
||||
for (i = 0; i < input_ports[0]->size; i++) {
|
||||
fprintf(fp, "assign %s__%s[%d] = %s__%s_%d_;\n",
|
||||
port_prefix, pb_type_input_ports[0]->name, i,
|
||||
port_prefix, pb_type_input_ports[0]->name, i);
|
||||
}
|
||||
/* Specify outputs are wires */
|
||||
pb_type_output_ports = find_pb_type_ports_match_spice_model_port_type(cur_pb_type, SPICE_MODEL_PORT_OUTPUT, &num_pb_type_output_port);
|
||||
for (i = 0; i < num_pb_type_output_port; i++) {
|
||||
fprintf(fp, "wire [0:%d] %s__%s;\n",
|
||||
output_ports[i]->size - 1, port_prefix, pb_type_output_ports[i]->name);
|
||||
}
|
||||
/* Make sure we have the same number outputs */
|
||||
assert (num_pb_type_output_port == num_output_port);
|
||||
for (i = 0; i < num_output_port; i++) {
|
||||
for (ipin = 0; ipin < output_ports[i]->size; ipin++) {
|
||||
fprintf(fp, "assign %s__%s_%d_ = %s__%s[%d];\n",
|
||||
port_prefix, pb_type_output_ports[i]->name, ipin,
|
||||
port_prefix, pb_type_output_ports[i]->name, ipin);
|
||||
}
|
||||
}
|
||||
|
||||
/* Specify SRAM output are wires */
|
||||
cur_num_sram = get_sram_orgz_info_num_mem_bit(cur_sram_orgz_info);
|
||||
dump_verilog_sram_config_bus_internal_wires(fp, cur_sram_orgz_info, cur_num_sram, cur_num_sram + num_sram - 1);
|
||||
|
@ -640,7 +614,7 @@ void dump_verilog_pb_primitive_lut(t_sram_orgz_info* cur_sram_orgz_info,
|
|||
/* Connect inputs*/
|
||||
/* Connect outputs*/
|
||||
fprintf(fp, "//----- Input and output ports -----\n");
|
||||
dump_verilog_pb_type_bus_ports(fp, port_prefix, 0, cur_pb_type, FALSE, TRUE);
|
||||
dump_verilog_pb_type_bus_ports(fp, port_prefix, 1, cur_pb_type, FALSE, TRUE, verilog_model->dump_explicit_port_map);
|
||||
fprintf(fp, "\n//----- SRAM ports -----\n");
|
||||
|
||||
/* check */
|
||||
|
|
|
@ -3029,7 +3029,7 @@ void dump_verilog_connection_box_mux(t_sram_orgz_info* cur_sram_orgz_info,
|
|||
verilog_model->prefix, mux_size, verilog_model->cnt, input_cnt);
|
||||
int drive_node_index = rr_gsb.get_cb_chan_node_index(cb_type, drive_rr_nodes[inode]);
|
||||
assert (-1 != drive_node_index);
|
||||
fprintf(fp, "%s;", rr_gsb.gen_cb_verilog_routing_track_name(cb_type, drive_node_index));
|
||||
fprintf(fp, "%s;\n", rr_gsb.gen_cb_verilog_routing_track_name(cb_type, drive_node_index));
|
||||
input_cnt++;
|
||||
}
|
||||
assert(input_cnt == mux_size);
|
||||
|
@ -4076,14 +4076,10 @@ void dump_verilog_routing_resources(t_sram_orgz_info* cur_sram_orgz_info,
|
|||
DeviceCoordinator cb_range = device_rr_gsb.get_gsb_range();
|
||||
|
||||
/* X - channels [1...nx][0..ny]*/
|
||||
for (int iy = 0; iy < (ny + 1); iy++) {
|
||||
for (int ix = 1; ix < (nx + 1); ix++) {
|
||||
for (size_t icb = 0; icb < device_rr_gsb.get_num_cb_unique_module(CHANX); ++icb) {
|
||||
const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(CHANX, icb);
|
||||
dump_verilog_routing_connection_box_unique_module(cur_sram_orgz_info, verilog_dir, subckt_dir, unique_mirror, CHANX);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* TODO: when we follow a tile organization,
|
||||
* updating the conf bits should follow a tile organization: CLB, SB and CBX, CBY */
|
||||
for (size_t ix = 0; ix < cb_range.get_x(); ++ix) {
|
||||
|
@ -4094,14 +4090,10 @@ void dump_verilog_routing_resources(t_sram_orgz_info* cur_sram_orgz_info,
|
|||
}
|
||||
|
||||
/* Y - channels [1...ny][0..nx]*/
|
||||
for (int ix = 0; ix < (nx + 1); ix++) {
|
||||
for (int iy = 1; iy < (ny + 1); iy++) {
|
||||
for (size_t icb = 0; icb < device_rr_gsb.get_num_cb_unique_module(CHANY); ++icb) {
|
||||
const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(CHANY, icb);
|
||||
dump_verilog_routing_connection_box_unique_module(cur_sram_orgz_info, verilog_dir, subckt_dir, unique_mirror, CHANY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t ix = 0; ix < cb_range.get_x(); ++ix) {
|
||||
for (size_t iy = 0; iy < cb_range.get_y(); ++iy) {
|
||||
|
|
|
@ -1439,10 +1439,24 @@ void dump_verilog_formal_verification_sram_ports_wiring(FILE* fp,
|
|||
t_sram_orgz_info* cur_sram_orgz_info,
|
||||
int sram_lsb, int sram_msb) {
|
||||
fprintf(fp, "assign ");
|
||||
|
||||
switch (cur_sram_orgz_info->type) {
|
||||
case SPICE_SRAM_STANDALONE:
|
||||
break;
|
||||
case SPICE_SRAM_SCAN_CHAIN:
|
||||
dump_verilog_sram_one_local_outport(fp, cur_sram_orgz_info,
|
||||
sram_lsb, sram_msb,
|
||||
0, VERILOG_PORT_CONKT);
|
||||
break;
|
||||
case SPICE_SRAM_MEMORY_BANK:
|
||||
dump_verilog_sram_one_outport(fp, cur_sram_orgz_info,
|
||||
sram_lsb, sram_msb,
|
||||
0, VERILOG_PORT_CONKT);
|
||||
break;
|
||||
default:
|
||||
vpr_printf(TIO_MESSAGE_ERROR, "(File:%s,[LINE%d])Invalid SRAM organization type!\n",
|
||||
__FILE__, __LINE__);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fprintf(fp, " = ");
|
||||
|
||||
|
|
|
@ -201,6 +201,7 @@ static void power_usage_primitive(t_power_usage * power_usage, t_pb * pb,
|
|||
}
|
||||
}
|
||||
|
||||
static
|
||||
void power_usage_local_pin_toggle(t_power_usage * power_usage, t_pb * pb,
|
||||
t_pb_graph_pin * pin) {
|
||||
float scale_factor;
|
||||
|
@ -222,6 +223,7 @@ void power_usage_local_pin_toggle(t_power_usage * power_usage, t_pb * pb,
|
|||
/ g_solution_inf.T_crit;
|
||||
}
|
||||
|
||||
static
|
||||
void power_usage_local_pin_buffer_and_wire(t_power_usage * power_usage,
|
||||
t_pb * pb, t_pb_graph_pin * pin) {
|
||||
t_power_usage sub_power_usage;
|
||||
|
@ -1027,6 +1029,7 @@ static void power_usage_routing(t_power_usage * power_usage,
|
|||
}
|
||||
}
|
||||
|
||||
static
|
||||
void power_alloc_and_init_pb_pin(t_pb_graph_pin * pin) {
|
||||
int port_idx;
|
||||
t_port * port_to_find;
|
||||
|
@ -1085,6 +1088,7 @@ void power_alloc_and_init_pb_pin(t_pb_graph_pin * pin) {
|
|||
}
|
||||
}
|
||||
|
||||
static
|
||||
void power_init_pb_pins_rec(t_pb_graph_node * pb_node) {
|
||||
int mode;
|
||||
int type;
|
||||
|
@ -1131,6 +1135,7 @@ void power_init_pb_pins_rec(t_pb_graph_node * pb_node) {
|
|||
}
|
||||
}
|
||||
|
||||
static
|
||||
void power_pb_pins_init() {
|
||||
int type_idx;
|
||||
|
||||
|
@ -1141,6 +1146,7 @@ void power_pb_pins_init() {
|
|||
}
|
||||
}
|
||||
|
||||
static
|
||||
void power_routing_init(t_det_routing_arch * routing_arch) {
|
||||
int net_idx;
|
||||
int rr_node_idx;
|
||||
|
@ -1367,7 +1373,7 @@ boolean power_uninit(void) {
|
|||
}
|
||||
delete mux_info;
|
||||
}
|
||||
free(g_power_commonly_used);
|
||||
delete g_power_commonly_used;
|
||||
|
||||
if (g_power_output->out) {
|
||||
fclose(g_power_output->out);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// Coder : Xifan TANG
|
||||
//-----------------------------------------------------
|
||||
//------ Include defines: preproc flags -----
|
||||
`include "OPENFPGAPATHKEYWORD/vpr7_x2p/vpr/test_modes_Verilog/SRC/fpga_defines.v"
|
||||
`include "GENERATED_DIR_KEYWORD/SRC/fpga_defines.v"
|
||||
module static_dff (
|
||||
/* Global ports go first */
|
||||
input set, // set input
|
||||
|
|
|
@ -16,21 +16,25 @@ act_file="${OpenFPGA_path}/fpga_flow/benchmarks/Blif/Test_Modes/$benchmark.act "
|
|||
verilog_reference="${OpenFPGA_path}/fpga_flow/benchmarks/Verilog/Test_Modes/$benchmark.v"
|
||||
vpr_route_chan_width="200"
|
||||
fpga_flow_script="${OpenFPGA_path}/fpga_flow/scripts"
|
||||
ff_path="$vpr_path/VerilogNetlists/ff.v"
|
||||
new_ff_path="$verilog_output_dirpath/$verilog_output_dirname/SRC/ff.v"
|
||||
ff_keyword="GENERATED_DIR_KEYWORD"
|
||||
ff_include_path="$verilog_output_dirpath/$verilog_output_dirname"
|
||||
arch_ff_keyword="FFPATHKEYWORD"
|
||||
|
||||
# Step A: Make sure a clean start
|
||||
# Recompile if needed
|
||||
#make clean
|
||||
#make -j32
|
||||
# Remove previous designs
|
||||
rm -rf $verilog_output_dirpath/$verilog_output_dirname
|
||||
|
||||
mkdir ${OpenFPGA_path}/fpga_flow/arch/generated
|
||||
|
||||
cd $fpga_flow_scripts
|
||||
perl arch_rewrite.pl -i $template_arch_xml_file -o $arch_xml_file
|
||||
perl rewrite_path_in_file.pl -i $template_arch_xml_file -o $arch_xml_file
|
||||
perl rewrite_path_in_file.pl -i $arch_xml_file -k $arch_ff_keyword $new_ff_path
|
||||
cd -
|
||||
|
||||
# Run VPR
|
||||
#valgrind
|
||||
./vpr $arch_xml_file $blif_file --full_stats --nodisp --activity_file $act_file --fpga_verilog --fpga_verilog_dir $verilog_output_dirpath/$verilog_output_dirname --fpga_x2p_rename_illegal_port --fpga_bitstream_generator --fpga_verilog_print_top_testbench --fpga_verilog_print_input_blif_testbench --fpga_verilog_include_timing --fpga_verilog_include_signal_init --fpga_verilog_print_formal_verification_top_netlist --fpga_verilog_print_autocheck_top_testbench $verilog_reference --fpga_verilog_print_user_defined_template --route_chan_width $vpr_route_chan_width --fpga_verilog_include_icarus_simulator --fpga_verilog_print_report_timing_tcl --power --tech_properties $tech_file --fpga_verilog_print_sdc_pnr --fpga_verilog_print_sdc_analysis --fpga_x2p_compact_routing_hierarchy
|
||||
|
||||
cd $fpga_flow_scripts
|
||||
perl rewrite_path_in_file.pl -i $ff_path -o $new_ff_path -k $ff_keyword $ff_include_path
|
||||
cd -
|
||||
|
|
Loading…
Reference in New Issue