shuffle poset - initial commit
commit
918ebf5a8f
|
@ -0,0 +1,2 @@
|
|||
build/*
|
||||
papers/*
|
|
@ -0,0 +1,12 @@
|
|||
#!/bin/bash
|
||||
|
||||
root_path=$PWD
|
||||
opts_path="$root_path/bin/options"
|
||||
bld_path="$root_path/bin/bld"
|
||||
local_path="$root_path/local"
|
||||
src_path="$root_path/src"
|
||||
build_path="$root_path/build"
|
||||
program_pather="$local_path/exe_paths.txt"
|
||||
|
||||
. $local_path/local_vars.sh
|
||||
. $bld_path/bld_core.sh
|
|
@ -0,0 +1,26 @@
|
|||
#!/bin/bash
|
||||
|
||||
###### Parse Arguments ########################################################
|
||||
command=$1
|
||||
args=()
|
||||
for ((i=2; i<=$#; i+=1)); do
|
||||
args+=(${!i})
|
||||
done
|
||||
|
||||
###### Source the Core ########################################################
|
||||
bld_path="$(dirname $(realpath "$0"))"
|
||||
source "$bld_path/bld_core.sh"
|
||||
|
||||
###### Dispatch ###############################################################
|
||||
if [ "$command" == "cmp" ]; then
|
||||
bld_compile "${args[@]}"
|
||||
elif [ "$command" == "lnk" ]; then
|
||||
bld_link "${args[@]}"
|
||||
elif [ "$command" == "lib" ]; then
|
||||
bld_lib "${args[@]}"
|
||||
elif [ "$command" == "unit" ]; then
|
||||
bld_unit "${args[@]}"
|
||||
else
|
||||
echo "unknown command '$command'"
|
||||
fi
|
||||
|
|
@ -0,0 +1,757 @@
|
|||
#!/bin/bash
|
||||
|
||||
###### Usage ##################################################################
|
||||
# To use the bld build system in a bash script, some setup is required:
|
||||
# 1. Define paths:
|
||||
# root_path, opts_path, bld_path, local_path, src_path,
|
||||
# build_path, program_pather
|
||||
# 2. Define local variables:
|
||||
# compiler, compile_mode, arch, linker, ctx_opts
|
||||
# 3. Then include this core file with "." prefix:
|
||||
# . $bld_path/bld_core.sh
|
||||
#
|
||||
#
|
||||
# The following bld functions form the core "commands":
|
||||
# bld_compile, bld_link, bld_lib, bld_unit
|
||||
#
|
||||
# And there are several more helper functions:
|
||||
# bld_print_implicit_opts, bld_load_local_opts
|
||||
#
|
||||
#
|
||||
# Signatures:
|
||||
# bld_compile <source-file> <zero-or-more-options>
|
||||
# bld_link <out-name> <one-or-more-source-files-objs-or-libs> -- \
|
||||
# <zero-or-more-options>
|
||||
# bld_lib <out-name> <one-or-more-source-files-or-objs> -- \
|
||||
# <zero-or-more-options>
|
||||
# bld_unit <source-file> <zero-or-more-options>
|
||||
#
|
||||
# bld_print_implicit_opts (no arguments)
|
||||
# bld_load_local_opts (no arguments)
|
||||
#
|
||||
#
|
||||
# Shared notes for all:
|
||||
# + Options are gathered from the arguments, from the local context, and
|
||||
# in the case of the commands bld_compile and bld_unit, from the source file.
|
||||
# + Options gathered from local context are prefixed with their context
|
||||
# variable like so [var:val]. So for example, on windows the options list
|
||||
# includes [os:windows].
|
||||
# + Options in source files are specified between the strings //$ and //.
|
||||
# + Object file extensions should be named based on whether they were
|
||||
# generated by a compiler or assembler. Compiled objects should have the
|
||||
# extension .cmp:o and assembled objects should have the extension
|
||||
# .asm:o. The system automatically changes the name to match the correct
|
||||
# extension given the context.
|
||||
# + Static library files should have the extension .lib. The system
|
||||
# automatically changes the name to match the correct extension given the
|
||||
# context.
|
||||
# + When the option "diagnostics" is included, extra details will be printed
|
||||
# from the bld commands. Showing the full list of options, invokation lines,
|
||||
# and the build path.
|
||||
#
|
||||
# bld_compile:
|
||||
# + Creates an object file from a single source file via the selected compiler.
|
||||
#
|
||||
# bld_link:
|
||||
# + Creates executables and shared binaries from source files, object files,
|
||||
# and static libraries. First uses the bld_compile command on the source
|
||||
# files.
|
||||
# + Uses the selected linker.
|
||||
# + If the short name for the linker does not work for any reason, the
|
||||
# full path to the linker may be specified in a filter file pointed to by
|
||||
# the setup variable `program_pather`
|
||||
# + The output is a shared binary (.dll or .so) when the option "dll" is
|
||||
# included.
|
||||
#
|
||||
# bld_lib:
|
||||
# + Creates a static library from source files and object files. First uses
|
||||
# the bld_compile command on the source files. Uses the OS to determine the
|
||||
# correct archiver.
|
||||
#
|
||||
# bld_unit:
|
||||
# + Creates an executable (or shared binary) from a single source file.
|
||||
# This command essentially does a single bld_compile then bld_link. With
|
||||
# two differences:
|
||||
# 1. The options from the source file are visible to the bld_link.
|
||||
# 2. The name of the executable is determined either from the first option
|
||||
# in the list or from the source file name if there are no options.
|
||||
#
|
||||
# bld_print_implicit_opts:
|
||||
# + Shows the implicit options loaded from the local parameters script.
|
||||
#
|
||||
# bld_load_local_opts:
|
||||
# + It is possible and sometimes useful to modify the local parameters after
|
||||
# they are loaded to create certain kinds of build scripts. This function
|
||||
# resets the local parameters to their original states by rerunning the
|
||||
# local parameters script.
|
||||
|
||||
|
||||
###### Flags From Opts ########################################################
|
||||
|
||||
function bld_flags_from_opts {
|
||||
###### parse arguments ####################################################
|
||||
local in_file=$1
|
||||
local opts=()
|
||||
for ((i=2; i<=$#; i+=1)); do
|
||||
opts+=(${!i})
|
||||
done
|
||||
|
||||
###### load file ##########################################################
|
||||
local flags_raw=()
|
||||
IFS=$'\r\n' GLOBIGNORE='*' command eval 'flags_raw=($(cat $in_file))'
|
||||
|
||||
###### filter #############################################################
|
||||
local flags=()
|
||||
for ((i=0;i<${#flags_raw[@]};i+=1)); do
|
||||
local flag=${flags_raw[i]}
|
||||
|
||||
###### skip blanks and comments #######################################
|
||||
if [[ -z "${flag// }" ]]; then
|
||||
continue
|
||||
fi
|
||||
if [[ "${flag:0:1}" == "#" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
###### parse line filters #############################################
|
||||
local line_filters=()
|
||||
while [[ $flag = *">"* ]]; do
|
||||
line_filters+=("${flag%%>*}")
|
||||
flag="${flag#*>}"
|
||||
done
|
||||
|
||||
###### check filters ##################################################
|
||||
local can_include=1
|
||||
for ((j=0;j<${#line_filters[@]};j+=1)); do
|
||||
can_include=0
|
||||
for ((k=0;k<${#opts[@]};k+=1)); do
|
||||
if [[ ${opts[k]} = ${line_filters[j]} ]]; then
|
||||
can_include=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ "$can_include" = "0" ]]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ "$can_include" = "1" ]]; then
|
||||
flags+=("${flag}")
|
||||
fi
|
||||
done
|
||||
|
||||
echo "${flags[@]}"
|
||||
}
|
||||
|
||||
###### Opts From Src ##########################################################
|
||||
|
||||
function bld_opts_from_src {
|
||||
###### split file into tokens #############################################
|
||||
local in_file=$1
|
||||
local tokens=($(grep "//\\$" $in_file))
|
||||
|
||||
###### parse ##############################################################
|
||||
local in_params_range="0"
|
||||
local params=()
|
||||
for ((i=0; i<${#tokens[@]}; i+=1)); do
|
||||
local string="${tokens[i]}"
|
||||
if [[ "$in_params_range" == "0" ]]; then
|
||||
if [[ "$string" == "//$" ]]; then
|
||||
in_params_range="1"
|
||||
fi
|
||||
elif [[ "$in_params_range" == "1" ]]; then
|
||||
if [[ "${string:0:2}" == "//" ]]; then
|
||||
break
|
||||
fi
|
||||
params+=($string)
|
||||
fi
|
||||
done
|
||||
|
||||
echo "${params[@]}"
|
||||
}
|
||||
|
||||
###### Dedup ##################################################################
|
||||
|
||||
function bld_dedup {
|
||||
###### parse arguments ####################################################
|
||||
local in=()
|
||||
for ((i=1; i<=$#; i+=1)); do
|
||||
in+=(${!i})
|
||||
done
|
||||
|
||||
###### dedup ##############################################################
|
||||
local out=()
|
||||
for ((i=0; i<${#in[@]}; i+=1)); do
|
||||
local string=${in[i]}
|
||||
local is_dup="0"
|
||||
for ((j=0; j<${#out[@]}; j+=1)); do
|
||||
if [[ "$string" == "${out[j]}" ]]; then
|
||||
is_dup="1"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ "$is_dup" == "0" ]]; then
|
||||
out+=($string)
|
||||
fi
|
||||
done
|
||||
|
||||
echo "${out[@]}"
|
||||
}
|
||||
|
||||
###### Has Opt ################################################################
|
||||
|
||||
function bld_has_opt {
|
||||
###### parse arguments ####################################################
|
||||
local key_opt=$1
|
||||
local opts=()
|
||||
for ((i=2; i<=$#; i+=1)); do
|
||||
opts+=(${!i})
|
||||
done
|
||||
|
||||
###### scan ###############################################################
|
||||
local has_key=0
|
||||
for ((i=0;i<${#opts[@]};i+=1)); do
|
||||
local opt=${opts[i]}
|
||||
if [[ "$opt" == "$key_opt" ]]; then
|
||||
has_key=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
echo $has_key
|
||||
}
|
||||
|
||||
###### Load Local Options #####################################################
|
||||
|
||||
function bld_load_local_opts {
|
||||
###### os cracking ########################################################
|
||||
os="undefined"
|
||||
if [ "$OSTYPE" == "win32" ] ||
|
||||
[ "$OSTYPE" == "msys" ]; then
|
||||
os="windows"
|
||||
elif [ "$OSTYPE" == "linux-gnu" ]; then
|
||||
os="linux"
|
||||
elif [ "$OSTYPE" == "darwin" ]; then
|
||||
os="mac"
|
||||
fi
|
||||
###### load parameters from the local script ################################
|
||||
if [ -f "$local_path/local_vars.sh" ]; then
|
||||
source "$local_path/local_vars.sh"
|
||||
fi
|
||||
}
|
||||
|
||||
###### Implicit Opts ##########################################################
|
||||
|
||||
function bld_implicit_opts {
|
||||
local linker_opt=""
|
||||
if [[ "$linker" == "link" || "$linker" == "lld-link" ]]; then
|
||||
linker_opt="link:msvc"
|
||||
fi
|
||||
echo $ctx_opts cmp:$compiler mode:$compile_mode asm:$assembler
|
||||
echo link:$linker $linker_opt os:$os arch:$arch
|
||||
}
|
||||
|
||||
###### Print Implicit Options #################################################
|
||||
|
||||
function bld_print_implicit_opts {
|
||||
local opts=($(bld_implicit_opts))
|
||||
local bracketed=()
|
||||
for ((i=0; i<=${#opts[@]}; i+=1)); do
|
||||
local opt="${opts[i]}"
|
||||
if [ "$opt" != "" ]; then
|
||||
bracketed+=("[${opts[i]}]")
|
||||
fi
|
||||
done
|
||||
echo "${bracketed[@]}"
|
||||
}
|
||||
|
||||
###### Print Help #############################################################
|
||||
|
||||
function bld_print_obj_note {
|
||||
echo "NOTE: the interface does not use standard object file extensions"
|
||||
echo "NOTE: use 'cmp:o' for object files produced by a compiler"
|
||||
echo "NOTE: use 'asm:o' for object files produced by an assembler"
|
||||
}
|
||||
|
||||
###### Compile ################################################################
|
||||
|
||||
function bld_compile {
|
||||
local i=0
|
||||
|
||||
###### parse arguments ####################################################
|
||||
local in_file=$1
|
||||
local opts=()
|
||||
for ((i=2; i<=$#; i+=1)); do
|
||||
opts+=(${!i})
|
||||
done
|
||||
if [ "$in_file" == "" ]; then
|
||||
echo "ERROR(compile): missing input file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
###### finish in file #####################################################
|
||||
local final_in_file=$in_file
|
||||
|
||||
###### determine build kind ###############################################
|
||||
local ext="${final_in_file##*.}"
|
||||
local build_kind="undetermined"
|
||||
if [[ "$ext" == "c" || "$ext" == "cpp" ]]; then
|
||||
build_kind="compile"
|
||||
elif [[ "$ext" == "asm" ]]; then
|
||||
build_kind="assemble"
|
||||
else
|
||||
echo "ERROR(compile): unrecgonized source type $final_in_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
###### finish options #####################################################
|
||||
local src_opts=($(bld_opts_from_src $final_in_file))
|
||||
local impl_opts=($(bld_implicit_opts))
|
||||
local all_opts=($(bld_dedup ${impl_opts[@]} ${opts[@]} ${src_opts[@]}))
|
||||
|
||||
###### diagnostics ########################################################
|
||||
local diagnostics=$(bld_has_opt diagnostics ${all_opts[@]})
|
||||
|
||||
###### out file name ######################################################
|
||||
local dot_ext_o=""
|
||||
if [[ "$build_kind" == "compile" ]]; then
|
||||
dot_ext_o=$(bld_ext_cmpo)
|
||||
elif [[ "$build_kind" == "assemble" ]]; then
|
||||
dot_ext_o=$(bld_ext_asmo)
|
||||
fi
|
||||
local file_base=${final_in_file##*/}
|
||||
local file_base_no_ext=${file_base%.*}
|
||||
local out_file="$file_base_no_ext$dot_ext_o"
|
||||
|
||||
###### get real flags #####################################################
|
||||
local flags=""
|
||||
if [[ "$build_kind" == "compile" ]]; then
|
||||
flags=$(bld_flags_from_opts $opts_path/compiler_flags.txt ${all_opts[@]})
|
||||
else
|
||||
flags=$(bld_flags_from_opts $opts_path/assembler_flags.txt ${all_opts[@]})
|
||||
fi
|
||||
|
||||
###### move to output folder ##############################################
|
||||
mkdir -p "$build_path"
|
||||
cd $build_path
|
||||
if [ "$diagnostics" == "1" ]; then
|
||||
echo "build path: $build_path"
|
||||
fi
|
||||
|
||||
###### delete existing object file ########################################
|
||||
rm -f "$out_file_base.o"
|
||||
rm -f "$out_file_base.obj"
|
||||
|
||||
###### final flags ########################################################
|
||||
local final_flags=""
|
||||
if [[ "$build_kind" == "compile" ]]; then
|
||||
final_flags="-c -I$src_path ${flags}"
|
||||
elif [[ "$build_kind" == "assemble" ]]; then
|
||||
final_flags="-c ${flags}"
|
||||
fi
|
||||
|
||||
###### compile ############################################################
|
||||
if [[ "$build_kind" == "compile" ]]; then
|
||||
if [ "$diagnostics" == "1" ]; then
|
||||
echo "cmp $final_in_file -- ${all_opts[@]}"
|
||||
echo $compiler "$final_in_file" $final_flags
|
||||
fi
|
||||
if [ "$compiler" == "clang" ]; then
|
||||
echo "$file_base"
|
||||
fi
|
||||
$compiler "$final_in_file" $final_flags
|
||||
elif [[ "$build_kind" == "assemble" ]]; then
|
||||
if [ "$diagnostics" == "1" ]; then
|
||||
echo "asm $final_in_file -- ${all_opts[@]}"
|
||||
echo $assembler $final_flags "$final_in_file"
|
||||
fi
|
||||
$assembler $final_flags "$final_in_file"
|
||||
fi
|
||||
|
||||
# return of status from compiler is automatic here.
|
||||
}
|
||||
|
||||
###### Link ###################################################################
|
||||
|
||||
function bld_link {
|
||||
local i=0
|
||||
|
||||
###### parse arguments ####################################################
|
||||
local out_name=$1
|
||||
local in_files=()
|
||||
for ((i=2; i<=$#; i+=1)); do
|
||||
if [ "${!i}" == "--" ]; then
|
||||
break
|
||||
fi
|
||||
in_files+=(${!i})
|
||||
done
|
||||
local opts=()
|
||||
for ((i+=1; i<=$#; i+=1)); do
|
||||
opts+=(${!i})
|
||||
done
|
||||
if [ "$out_name" == "" ]; then
|
||||
echo "link: missing output name"
|
||||
return 1
|
||||
fi
|
||||
if [ "${#in_files}" == "0" ]; then
|
||||
echo "link: missing input file(s)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
###### finish options #####################################################
|
||||
local impl_opts=($(bld_implicit_opts))
|
||||
local all_opts=($(bld_dedup ${opts[@]} ${impl_opts[@]}))
|
||||
|
||||
###### diagnostics ########################################################
|
||||
local diagnostics=$(bld_has_opt diagnostics ${all_opts[@]})
|
||||
|
||||
###### sort in files ######################################################
|
||||
local in_src=()
|
||||
local in_cmpo=()
|
||||
local in_asmo=()
|
||||
local in_lib=()
|
||||
for ((i=0; i<${#in_files[@]}; i+=1)); do
|
||||
local file="${in_files[i]}"
|
||||
local ext="${file##*.}"
|
||||
if [[ "$ext" == "c" || "$ext" == "cpp" || "$ext" == "asm" ]]; then
|
||||
in_src+=($file)
|
||||
elif [[ "$ext" == "cmp:o" ]]; then
|
||||
in_cmpo+=($file)
|
||||
elif [[ "$ext" == "asm:o" ]]; then
|
||||
in_asmo+=($file)
|
||||
elif [[ "$ext" == "lib" ]]; then
|
||||
in_lib+=($file)
|
||||
else
|
||||
echo "WARNING: ignoring unrecgonized file type $file"
|
||||
if [[ "$ext" == "obj" || "$ext" == "o" ]]; then
|
||||
bld_print_obj_note
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
###### auto correct compiled object files #################################
|
||||
local dot_ext_cmpo=$(bld_ext_cmpo)
|
||||
for ((i=0; i<${#in_cmpo[@]}; i+=1)); do
|
||||
local file_name="${in_cmpo[i]}"
|
||||
local base_name="${file_name%.*}"
|
||||
in_cmpo[$i]="$base_name$dot_ext_cmpo"
|
||||
done
|
||||
|
||||
###### auto correct assembled object files ################################
|
||||
local dot_ext_asmo=$(bld_ext_asmo)
|
||||
for ((i=0; i<${#in_asmo[@]}; i+=1)); do
|
||||
local file_name="${in_asmo[i]}"
|
||||
local base_name="${file_name%.*}"
|
||||
in_asmo[$i]="$base_name$dot_ext_asmo"
|
||||
done
|
||||
|
||||
###### combine object files ###############################################
|
||||
local in_obj=()
|
||||
for ((i=0; i<${#in_cmpo[@]}; i+=1)); do
|
||||
local file="${in_cmpo[i]}"
|
||||
in_obj+=($file)
|
||||
done
|
||||
for ((i=0; i<${#in_asmo[@]}; i+=1)); do
|
||||
local file="${in_asmo[i]}"
|
||||
in_obj+=($file)
|
||||
done
|
||||
|
||||
###### compile source files ###############################################
|
||||
for ((i=0; i<${#in_src[@]}; i+=1)); do
|
||||
local file="${in_src[i]}"
|
||||
bld_compile "$file" ${all_opts[@]}
|
||||
local status=$?
|
||||
if [ $status -ne 0 ]; then
|
||||
return $status
|
||||
fi
|
||||
done
|
||||
|
||||
###### intermediate object files ##########################################
|
||||
local interm_obj=()
|
||||
for ((i=0; i<${#in_src[@]}; i+=1)); do
|
||||
local file_name="${in_src[i]}"
|
||||
local base_name="${file_name##*/}"
|
||||
local base_name_no_ext="${base_name%.*}"
|
||||
local ext="${base_name##*.}"
|
||||
if [[ "$ext" == "c" || "$ext" == "cpp" ]]; then
|
||||
interm_obj+=($base_name_no_ext$dot_ext_cmpo)
|
||||
elif [[ "$ext" == "asm" ]]; then
|
||||
interm_obj+=($base_name_no_ext$dot_ext_asmo)
|
||||
fi
|
||||
done
|
||||
|
||||
###### get real flags #####################################################
|
||||
local flags=$(bld_flags_from_opts $opts_path/linker_flags.txt ${all_opts[@]})
|
||||
|
||||
###### out file name ######################################################
|
||||
local dot_ext_out=""
|
||||
local is_dll=$(bld_has_opt dll ${all_opts[@]})
|
||||
if [ "$is_dll" == "0" ]; then
|
||||
dot_ext_out=$(bld_ext_exe)
|
||||
else
|
||||
dot_ext_out=$(bld_ext_dll)
|
||||
fi
|
||||
out_file="$out_name$dot_ext_out"
|
||||
|
||||
###### move to output folder ##############################################
|
||||
mkdir -p "$build_path"
|
||||
cd $build_path
|
||||
if [ "$diagnostics" == "1" ]; then
|
||||
echo "build path: $build_path"
|
||||
fi
|
||||
|
||||
###### final files to linker ##############################################
|
||||
local final_in_files="${interm_obj[@]} ${in_obj[@]} ${in_lib[@]}"
|
||||
|
||||
###### set first diagnostic string ########################################
|
||||
local first_diagnostic_string="lnk $final_in_files -- ${all_opts[@]}"
|
||||
|
||||
###### linker executable name #############################################
|
||||
local linker_exe=$linker
|
||||
local linker_rename=$(bld_flags_from_opts $program_pather "link:$linker")
|
||||
if [[ "$linker_rename" != "" ]]; then
|
||||
if [ "$diagnostics" == "1" ]; then
|
||||
echo "linker rename: $linker_rename"
|
||||
fi
|
||||
linker_exe=$linker_rename
|
||||
fi
|
||||
|
||||
###### link ###############################################################
|
||||
local status=0
|
||||
local invokation=""
|
||||
if [[ "$linker" == "link" || "$linker" == "lld-link" ]]; then
|
||||
invokation="-OUT:$out_file $flags $final_in_files"
|
||||
elif [ "$linker" == "clang" ]; then
|
||||
invokation="-o $out_file $flags $final_in_files"
|
||||
else
|
||||
echo "ERROR(link): invokation not defined for this linker"
|
||||
status=1
|
||||
fi
|
||||
if [ "$invokation" != "" ]; then
|
||||
if [ "$diagnostics" == "1" ]; then
|
||||
echo $first_diagnostic_string
|
||||
echo $linker_exe $invokation
|
||||
fi
|
||||
echo "$out_file"
|
||||
"$linker_exe" $invokation
|
||||
status=$?
|
||||
fi
|
||||
|
||||
return $status
|
||||
}
|
||||
|
||||
###### Library ################################################################
|
||||
|
||||
function bld_lib {
|
||||
local i=0
|
||||
|
||||
###### parse arguments ####################################################
|
||||
local out_name=$1
|
||||
local in_files=()
|
||||
for ((i=2; i<=$#; i+=1)); do
|
||||
if [ "${!i}" == "--" ]; then
|
||||
break
|
||||
fi
|
||||
in_files+=(${!i})
|
||||
done
|
||||
local opts=()
|
||||
for ((i+=1; i<=$#; i+=1)); do
|
||||
opts+=(${!i})
|
||||
done
|
||||
if [ "$out_name" == "" ]; then
|
||||
echo "lib: missing output name"
|
||||
return 1
|
||||
fi
|
||||
if [ "${#in_files}" == "0" ]; then
|
||||
echo "lib: missing input file(s)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
###### finish options #####################################################
|
||||
local impl_opts=($(bld_implicit_opts))
|
||||
local all_opts=($(bld_dedup ${opts[@]} ${impl_opts[@]}))
|
||||
|
||||
###### diagnostics ########################################################
|
||||
local diagnostics=$(bld_has_opt diagnostics ${all_opts[@]})
|
||||
|
||||
###### sort in files ######################################################
|
||||
local in_src=()
|
||||
local in_cmpo=()
|
||||
local in_asmo=()
|
||||
for ((i=0; i<${#in_files[@]}; i+=1)); do
|
||||
local file="${in_files[i]}"
|
||||
local ext="${file##*.}"
|
||||
if [[ "$ext" == "c" || "$ext" == "cpp" ]]; then
|
||||
in_src+=($file)
|
||||
elif [[ "$ext" == "cmp:o" ]]; then
|
||||
in_cmpo+=($file)
|
||||
elif [[ "$ext" == "asm:o" ]]; then
|
||||
in_asmo+=($file)
|
||||
else
|
||||
echo "WARNING: ignoring unrecgonized file type $file"
|
||||
if [[ "$ext" == "obj" || "$ext" == "o" ]]; then
|
||||
bld_print_obj_note
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
###### auto correct compiled object files #################################
|
||||
local dot_ext_cmpo=$(bld_ext_cmpo)
|
||||
for ((i=0; i<${#in_cmpo[@]}; i+=1)); do
|
||||
local file_name="${in_cmpo[i]}"
|
||||
local base_name="${file_name%.*}"
|
||||
in_cmpo[$i]=$base_name$dot_ext_cmpo
|
||||
done
|
||||
|
||||
###### auto correct assembled object files ################################
|
||||
local dot_ext_asmo=$(bld_ext_asmo)
|
||||
for ((i=0; i<${#in_asmo[@]}; i+=1)); do
|
||||
local file_name="${in_asmo[i]}"
|
||||
local base_name="${file_name%.*}"
|
||||
in_asmo[$i]="$base_name$dot_ext_asmo"
|
||||
done
|
||||
|
||||
###### combine object files ###############################################
|
||||
local in_obj=()
|
||||
for ((i=0; i<${#in_cmpo[@]}; i+=1)); do
|
||||
local file="${in_cmpo[i]}"
|
||||
in_obj+=($file)
|
||||
done
|
||||
for ((i=0; i<${#in_asmo[@]}; i+=1)); do
|
||||
local file="${in_asmo[i]}"
|
||||
in_obj+=($file)
|
||||
done
|
||||
|
||||
###### compile source files ###############################################
|
||||
for ((i=0; i<${#in_src[@]}; i+=1)); do
|
||||
bld_compile "${in_src[i]}" ${all_opts[@]}
|
||||
local status=$?
|
||||
if [ $status -ne 0 ]; then
|
||||
return $status
|
||||
fi
|
||||
done
|
||||
|
||||
###### intermediate object files ##########################################
|
||||
local interm_obj=()
|
||||
for ((i=0; i<${#in_src[@]}; i+=1)); do
|
||||
local file_name="${in_src[i]}"
|
||||
local base_name="${file_name##*/}"
|
||||
local base_name_no_ext="${base_name%.*}"
|
||||
local ext="${base_name##*.}"
|
||||
if [[ "$ext" == "c" || "$ext" == "cpp" ]]; then
|
||||
interm_obj+=($base_name_no_ext$dot_ext_cmpo)
|
||||
elif [[ "$ext" == "asm" ]]; then
|
||||
interm_obj+=($base_name_no_ext$dot_ext_asmo)
|
||||
fi
|
||||
done
|
||||
|
||||
###### out file name ######################################################
|
||||
local out_file=""
|
||||
if [ "$os" == "windows" ]; then
|
||||
out_file="$out_name.lib"
|
||||
elif [ "$os" == "linux" || "$os" == "mac" ]; then
|
||||
out_file="lib$out_name.a"
|
||||
else
|
||||
echo "ERROR(lib): static library output not defined for OS: $os"
|
||||
fi
|
||||
|
||||
###### final library build input files ####################################
|
||||
local final_in_files="${interm_obj[@]} ${in_obj[@]}"
|
||||
|
||||
###### move to output folder ##############################################
|
||||
mkdir -p "$build_path"
|
||||
cd $build_path
|
||||
if [ "$diagnostics" == "1" ]; then
|
||||
echo "build path: $build_path"
|
||||
fi
|
||||
|
||||
###### set first diagnostic string ########################################
|
||||
local first_diagnostic_string="lib $final_in_files -- ${all_opts[@]}"
|
||||
|
||||
###### build library ######################################################
|
||||
local status=0
|
||||
if [ "$os" == "windows" ]; then
|
||||
if [ "$diagnostics" == "1" ]; then
|
||||
echo $first_diagnostic_string
|
||||
echo lib -nologo -OUT:"$out_file" $final_in_files
|
||||
fi
|
||||
echo "$out_file"
|
||||
lib -nologo -OUT:"$out_file" $final_in_files
|
||||
status=$?
|
||||
elif [ "$os" == "linux" || "$os" == "mac" ]; then
|
||||
# TODO(allen): invoke ar here - make sure to delete the original .a first
|
||||
# because ar does not (seem) to replace the output file, just append
|
||||
echo "TODO: implement ar path in bld_core.sh:bld_lib"
|
||||
status=1
|
||||
else
|
||||
echo "ERROR(lib): static library invokation not defined for OS: $os"
|
||||
status=1
|
||||
fi
|
||||
|
||||
return $status
|
||||
}
|
||||
|
||||
###### Unit ###################################################################
|
||||
|
||||
function bld_unit {
|
||||
local i=0
|
||||
|
||||
###### parse arguments ####################################################
|
||||
local main_file=$1
|
||||
local opts=()
|
||||
for ((i=2; i<=$#; i+=1)); do
|
||||
opts+=(${!i})
|
||||
done
|
||||
if [ "$main_file" == "" ]; then
|
||||
echo "unit: missing main file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
###### set out name #######################################################
|
||||
local out_name=""
|
||||
if [ "${#opts}" == "0" ]; then
|
||||
local file_base=${main_file##*/}
|
||||
local file_base_no_ext=${file_base%.*}
|
||||
out_name=$file_base_no_ext
|
||||
else
|
||||
out_name="${opts[0]}"
|
||||
fi
|
||||
|
||||
###### finish options #####################################################
|
||||
local src_opts=$(bld_opts_from_src $main_file)
|
||||
local impl_opts=($(bld_implicit_opts))
|
||||
local all_opts=($(bld_dedup $out_name ${opts[@]} ${src_opts[@]} ${impl_opts[@]}))
|
||||
|
||||
###### link ###############################################################
|
||||
bld_link $out_name $main_file ${in_files[@]} -- ${all_opts[@]}
|
||||
}
|
||||
|
||||
###### Special Ifs ############################################################
|
||||
|
||||
function bld_ext_cmpo {
|
||||
echo $(bld_flags_from_opts $opts_path/file_extensions.txt cmp:$compiler "cmp:o")
|
||||
}
|
||||
function bld_ext_asmo {
|
||||
echo $(bld_flags_from_opts $opts_path/file_extensions.txt asm:$assembler "asm:o")
|
||||
}
|
||||
function bld_ext_exe {
|
||||
echo $(bld_flags_from_opts $opts_path/file_extensions.txt os:$os exe)
|
||||
}
|
||||
function bld_ext_dll {
|
||||
echo $(bld_flags_from_opts $opts_path/file_extensions.txt os:$os dll)
|
||||
}
|
||||
|
||||
###### Load Locals ############################################################
|
||||
bld_load_local_opts
|
||||
|
||||
###############################################################################
|
||||
# TODO: Notes for future itations on this sytem
|
||||
# - With the addition of the assembler, it is now becoming clear that the
|
||||
# bld_compile path is feeling a bit overloaded and that the mapping of
|
||||
# interface extensions to real extensions, and the mapping of interface
|
||||
# extensions to inferred build steps, are major complicating factors.
|
||||
# A new strategy for organizing multiple builders, and multiple conversion
|
||||
# paths that may be inferred from a set of possible starting points would
|
||||
# stand to simplify a lot of the system.
|
||||
# - Switching out object file extensions
|
||||
# - Subtlties of invoking different build kinds in bld_compile
|
||||
# - Tracking subkinds through bld_link and bld_lib
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
. bin/begin_bld_session.sh
|
||||
bld_print_implicit_opts
|
||||
|
||||
bld_unit /c/mr4th/mr4th/src/mr4th_base.target.c mr4th_base -- dll
|
||||
|
||||
bld_unit $src_path/shuffle_poset.c shuffle_poset
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
link:link>???
|
||||
|
||||
## The MSVC linker has been known to reside at this/these location(s)
|
||||
## in some installations
|
||||
# /c/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.31.31103/bin/Hostx64/x64/link.exe
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
compiler="cl"
|
||||
compile_mode="debug"
|
||||
assembler="ml64"
|
||||
arch="x64"
|
||||
linker="link"
|
||||
ctx_opts=""
|
|
@ -0,0 +1,2 @@
|
|||
asm:ml64>-nologo
|
||||
asm:ml64>mode:debug>-Zi
|
|
@ -0,0 +1,41 @@
|
|||
###### Include Paths ##########################################################
|
||||
-I/c/mr4th/mr4th/src
|
||||
-I/c/mr4th/mr4th/src/dependencies
|
||||
freetype>-I/c/mr4th/mr4th/src/dependencies/freetype-2.12.1/include
|
||||
|
||||
|
||||
###### CL #####################################################################
|
||||
cmp:cl>-nologo
|
||||
cmp:cl>-FC
|
||||
cmp:cl>-GR-
|
||||
cmp:cl>-EHa
|
||||
|
||||
cmp:cl>sanitizer>-fsanitize=address
|
||||
|
||||
cmp:cl>dll>-LD
|
||||
|
||||
###### Clang ##################################################################
|
||||
cmp:clang>-Wno-writable-strings
|
||||
cmp:clang>-Wno-switch
|
||||
cmp:clang>-Wno-deprecated-declarations
|
||||
|
||||
cmp:clang>sanitizer>-fsanitize=address
|
||||
cmp:clang>sanitizer>-fsanitize=undefined
|
||||
cmp:clang>os:linux>sanitizer>-fsanitize=safe-stack
|
||||
cmp:clang>fuzzer>-fsanitize=fuzzer
|
||||
|
||||
# TODO: DLL support
|
||||
|
||||
###### Debug ##################################################################
|
||||
mode:debug>-DMR4TH_ASSERTS=1
|
||||
mode:release>-DMR4TH_ASSERTS=0
|
||||
cmp:cl>mode:debug>-Zi
|
||||
cmp:clang>mode:debug>-g
|
||||
|
||||
sanitizer>-DMR4TH_SANITIZER=1
|
||||
|
||||
###### Profiling ##############################################################
|
||||
manualprofile>-DMR4TH_PROFILING_MANUAL=1
|
||||
|
||||
autoprofile>-DMR4TH_PROFILING_AUTO=1
|
||||
cmp:clang>autoprofile>-finstrument-functions
|
|
@ -0,0 +1,12 @@
|
|||
cmp:o>cmp:cl>.obj
|
||||
cmp:o>cmp:clang>.o
|
||||
cmp:o>cmp:gcc>.o
|
||||
|
||||
asm:o>asm:ml64>.obj
|
||||
|
||||
exe>os:windows>.exe
|
||||
dll>os:windows>.dll
|
||||
dll>os:linux>.so
|
||||
dll>os:mac>.so
|
||||
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
###### MSVC ###################################################################
|
||||
link:msvc>-nologo
|
||||
link:msvc>-DEFAULTLIB:libcmt
|
||||
link:msvc>-OPT:REF
|
||||
link:msvc>-INCREMENTAL:NO
|
||||
link:msvc>-DEBUG
|
||||
link:msvc>arch:x64>-MACHINE:X64
|
||||
link:msvc>console>-SUBSYSTEM:CONSOLE
|
||||
link:msvc>console_graphical>-SUBSYSTEM:CONSOLE
|
||||
link:msvc>graphical>-SUBSYSTEM:WINDOWS
|
||||
link:msvc>dll>-DLL
|
||||
|
||||
###### Clang ##################################################################
|
||||
link:clang>mode:debug>-g
|
||||
link:clang>dll>-shared
|
||||
|
||||
###### Windows ################################################################
|
||||
|
||||
link:msvc>os:windows>Winmm.lib
|
||||
link:msvc>os:windows>Userenv.lib
|
||||
link:msvc>os:windows>Advapi32.lib
|
||||
link:msvc>os:windows>User32.lib
|
||||
link:msvc>os:windows>graphical>Gdi32.lib
|
||||
link:msvc>os:windows>graphical>Dwmapi.lib
|
||||
link:msvc>os:windows>console_graphical>Gdi32.lib
|
||||
link:msvc>os:windows>console_graphical>Dwmapi.lib
|
||||
link:msvc>os:windows>audio>Ole32.lib
|
||||
link:msvc>os:windows>audio>onecore.lib
|
||||
link:msvc>os:windows>audio>Avrt.lib
|
||||
|
||||
link:clang>os:windows>-lWinmm.lib
|
||||
link:clang>os:windows>-lUserenv.lib
|
||||
link:clang>os:windows>-lAdvapi32.lib
|
||||
link:clang>os:windows>-lUser32.lib
|
||||
link:clang>os:windows>graphical>-lGdi32.lib
|
||||
link:clang>os:windows>graphical>-lDwmapi.lib
|
||||
link:clang>os:windows>console_graphical>-lGdi32.lib
|
||||
link:clang>os:windows>console_graphical>-lDwmapi.lib
|
||||
link:clang>os:windows>audio>-lOle32.lib
|
||||
link:clang>os:windows>audio>-lonecore.lib
|
||||
link:clang>os:windows>audio>-lAvrt.lib
|
||||
|
||||
link:msvc>freetype>freetype.lib
|
||||
link:clang>freetype>-lfreetype.lib
|
||||
|
||||
spall>base_profiling_by_spall.o
|
||||
|
||||
###### Sanitizer ##############################################################
|
||||
|
||||
link:clang>sanitizer>-fsanitize=address
|
||||
link:clang>sanitizer>-fsanitize=undefined
|
||||
link:clang>os:linux>sanitizer>-fsanitize=safe-stack
|
||||
link:clang>fuzzer>-fsanitize=fuzzer
|
|
@ -0,0 +1,6 @@
|
|||
link:link>/c/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.31.31103/bin/Hostx64/x64/link.exe
|
||||
|
||||
## The MSVC linker has been known to reside at this/these location(s)
|
||||
## in some installations
|
||||
# /c/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.31.31103/bin/Hostx64/x64/link.exe
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
compiler="cl"
|
||||
compile_mode="debug"
|
||||
assembler="ml64"
|
||||
arch="x64"
|
||||
linker="link"
|
||||
ctx_opts=""
|
|
@ -0,0 +1,35 @@
|
|||
version(2);
|
||||
project_name = "shuffle_poset";
|
||||
patterns = {
|
||||
"*.c",
|
||||
"*.cpp",
|
||||
"*.h",
|
||||
"*.m",
|
||||
"*.bat",
|
||||
"*.sh",
|
||||
"*.4coder",
|
||||
};
|
||||
blacklist_patterns = {
|
||||
".*",
|
||||
};
|
||||
load_paths_base = {
|
||||
{ ".", .relative = true, .recursive = true, },
|
||||
};
|
||||
load_paths = {
|
||||
.win = load_paths_base,
|
||||
.linux = load_paths_base,
|
||||
.mac = load_paths_base,
|
||||
};
|
||||
|
||||
commands = {
|
||||
.build = { .out = "*compilation*", .footer_panel = true,
|
||||
.save_dirty_files = true,
|
||||
.win = "git_bash bin/build.sh", },
|
||||
.run = { .out = "*run*", .footer_panel = false,
|
||||
.save_dirty_files = false,
|
||||
.win = "build\\shuffle_poset", },
|
||||
};
|
||||
fkey_command = {
|
||||
.F1 = "build",
|
||||
.F2 = "run",
|
||||
};
|
|
@ -0,0 +1,449 @@
|
|||
/*
|
||||
**
|
||||
** Shuffle Poset Implementation & Test
|
||||
**
|
||||
*/
|
||||
|
||||
#include "mr4th_base.h"
|
||||
#include "mr4th_base.stdio.h"
|
||||
#include "mr4th_keywords.h"
|
||||
#include "mr4th_prng.h"
|
||||
|
||||
#include "shuffle_poset.h"
|
||||
|
||||
#include "mr4th_base.c"
|
||||
#include "mr4th_prng.c"
|
||||
|
||||
////////////////////////////////
|
||||
// Functions
|
||||
|
||||
// poset
|
||||
|
||||
function POSET_Loose*
|
||||
poset_new(Arena *arena, U32 element_count){
|
||||
POSET_Loose *poset = push_array(arena, POSET_Loose, 1);
|
||||
poset->element_count = element_count;
|
||||
return(poset);
|
||||
}
|
||||
|
||||
function void
|
||||
poset_order(Arena *arena, POSET_Loose *poset, U32 ls, U32 gr){
|
||||
Assert(ls < poset->element_count);
|
||||
Assert(gr < poset->element_count);
|
||||
|
||||
POSET_Pair *pair = push_array(arena, POSET_Pair, 1);
|
||||
SLLQueuePush(poset->first, poset->last, pair);
|
||||
poset->pair_count += 1;
|
||||
pair->ls = ls;
|
||||
pair->gr = gr;
|
||||
}
|
||||
|
||||
function POSET*
|
||||
poset_bake(Arena *arena, POSET_Loose *loose){
|
||||
U32 element_count = loose->element_count;
|
||||
POSET *poset = push_array(arena, POSET, 1);
|
||||
S8 *compare_grid = push_array(arena, S8, element_count*element_count);
|
||||
|
||||
// write pairs into compare grid
|
||||
for (POSET_Pair *pair = loose->first;
|
||||
pair != 0;
|
||||
pair = pair->next){
|
||||
compare_grid[pair->ls + pair->gr*element_count] = -1;
|
||||
compare_grid[pair->gr + pair->ls*element_count] = +1;
|
||||
}
|
||||
|
||||
// close the graph paths
|
||||
for (;;){
|
||||
B32 update = 0;
|
||||
for (U32 i = 0; i < element_count; i += 1){
|
||||
for (U32 j = i + 1; j < element_count; j += 1){
|
||||
for (U32 k = j + 1; k < element_count; k += 1){
|
||||
if (compare_grid[i + j*element_count] < 0 &&
|
||||
compare_grid[j + k*element_count] < 0){
|
||||
if (compare_grid[i + k*element_count] == 0){
|
||||
compare_grid[i + k*element_count] = -1;
|
||||
compare_grid[k + i*element_count] = +1;
|
||||
update = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!update){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
poset->compare = compare_grid;
|
||||
poset->element_count = element_count;
|
||||
return(poset);
|
||||
}
|
||||
|
||||
function U32*
|
||||
poset_topological_sort(Arena *arena, POSET *poset){
|
||||
U32 element_count = poset->element_count;
|
||||
|
||||
// initialize array {0,...,n-1}
|
||||
U32 *array = push_array(arena, U32, element_count);
|
||||
for (U32 i = 0; i < element_count; i += 1){
|
||||
array[i] = i;
|
||||
}
|
||||
|
||||
// topological sort
|
||||
for (U32 i = 0; i + 1 < element_count; i += 1){
|
||||
U32 j = i;
|
||||
U32 je = array[j];
|
||||
for (U32 k = j + 1; k < element_count; k += 1){
|
||||
U32 ke = array[k];
|
||||
if (poset->compare[je + ke*element_count] > 0){
|
||||
j = k;
|
||||
je = ke;
|
||||
}
|
||||
}
|
||||
Swap(U32, array[i], array[j]);
|
||||
}
|
||||
|
||||
return(array);
|
||||
}
|
||||
|
||||
function U32*
|
||||
poset_shuffle(Arena *arena, PRNG *prng, POSET *poset){
|
||||
U32 element_count = poset->element_count;
|
||||
U32 *array = poset_topological_sort(arena, poset);
|
||||
|
||||
F32 n = (F32)element_count;
|
||||
F32 t = (4.f/(pi_F32*pi_F32)*n*n*n*ln_F32(n));
|
||||
U64 num_swaps = (U64)ceil_F32(t);
|
||||
U64 num_passes = CeilIntDiv(num_swaps, element_count);
|
||||
|
||||
for (U64 p = 0; p < num_passes; p += 1){
|
||||
// even pass
|
||||
for (U32 i = 0; i + 1 < element_count; i += 2){
|
||||
U32 je = array[i];
|
||||
U32 ke = array[i + 1];
|
||||
if (poset->compare[je + ke*element_count] == 0 &&
|
||||
prng_roll_bounded(prng, 2)){
|
||||
Swap(U32, array[i], array[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
// odd pass
|
||||
for (U32 i = 1; i + 1 < element_count; i += 2){
|
||||
U32 je = array[i];
|
||||
U32 ke = array[i + 1];
|
||||
if (poset->compare[je + ke*element_count] == 0 &&
|
||||
prng_roll_bounded(prng, 2)){
|
||||
Swap(U32, array[i], array[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return(array);
|
||||
}
|
||||
|
||||
// distribution
|
||||
|
||||
function DISTRIBUTION*
|
||||
distribution_new(Arena *arena, U32 element_count){
|
||||
DISTRIBUTION *distribution = push_array(arena, DISTRIBUTION, 1);
|
||||
distribution->element_count = element_count;
|
||||
return(distribution);
|
||||
}
|
||||
|
||||
function void
|
||||
distribution_add(Arena *arena, DISTRIBUTION *distribution, F32 weight, ...){
|
||||
ArenaTemp scratch = arena_get_scratch(&arena, 1);
|
||||
|
||||
U32 element_count = distribution->element_count;
|
||||
U32 *order = push_array(scratch.arena, U32, element_count);
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, weight);
|
||||
for (U32 idx = 0; idx < distribution->element_count; idx += 1){
|
||||
U32 x = va_arg(args, U32);
|
||||
if (x = max_U32){
|
||||
break;
|
||||
}
|
||||
order[idx] = x;
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
distribution_add_order(arena, distribution, weight, order);
|
||||
|
||||
arena_release_scratch(&scratch);
|
||||
}
|
||||
|
||||
function void
|
||||
distribution_add_order(Arena *arena, DISTRIBUTION *distribution, F32 weight, U32 *order){
|
||||
U32 element_count = distribution->element_count;
|
||||
|
||||
U32 hash = 1337;
|
||||
for (U32 idx = 0; idx < element_count; idx += 1){
|
||||
hash = (hash << 5) - hash + (idx + 1)*order[idx];
|
||||
}
|
||||
|
||||
U32 bucket_idx = hash%ArrayCount(distribution->buckets);
|
||||
DISTRIBUTION_Bucket *match_bucket = 0;
|
||||
for (DISTRIBUTION_Bucket *bucket = distribution->buckets[bucket_idx];
|
||||
bucket != 0;
|
||||
bucket = bucket->next_hash){
|
||||
if (bucket->hash == hash &&
|
||||
memcmp(bucket->order, order, sizeof(*order)*element_count) == 0){
|
||||
match_bucket = bucket;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match_bucket == 0){
|
||||
match_bucket = push_array(arena, DISTRIBUTION_Bucket, 1);
|
||||
SLLQueuePush(distribution->first, distribution->last, match_bucket);
|
||||
|
||||
U32 *order_cpy = push_array(arena, U32, element_count);
|
||||
MemoryCopy(order_cpy, order, sizeof(*order)*element_count);
|
||||
|
||||
match_bucket->order = order_cpy;
|
||||
match_bucket->hash = hash;
|
||||
|
||||
SLLStackPush_N(distribution->buckets[bucket_idx], match_bucket, next_hash);
|
||||
|
||||
distribution->bucket_count += 1;
|
||||
}
|
||||
|
||||
match_bucket->weight += weight;
|
||||
}
|
||||
|
||||
|
||||
function void
|
||||
distribution_normalize(DISTRIBUTION *distribution){
|
||||
F32 total_weight = 0.f;
|
||||
for (DISTRIBUTION_Bucket *bucket = distribution->first;
|
||||
bucket != 0;
|
||||
bucket = bucket->next){
|
||||
total_weight += bucket->weight;
|
||||
}
|
||||
|
||||
if (total_weight > 0.f){
|
||||
F32 norm = 1.f/total_weight;
|
||||
for (DISTRIBUTION_Bucket *bucket = distribution->first;
|
||||
bucket != 0;
|
||||
bucket = bucket->next){
|
||||
bucket->weight *= norm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function void
|
||||
distribution_sort(DISTRIBUTION *distribution){
|
||||
ArenaTemp scratch = arena_get_scratch(0, 0);
|
||||
|
||||
// flatten buckets
|
||||
U32 bucket_count = distribution->bucket_count;
|
||||
DISTRIBUTION_Bucket **buckets =
|
||||
push_array(scratch.arena, DISTRIBUTION_Bucket*, bucket_count);
|
||||
{
|
||||
U32 idx = 0;
|
||||
for (DISTRIBUTION_Bucket *bucket = distribution->first;
|
||||
bucket != 0;
|
||||
bucket = bucket->next, idx += 1){
|
||||
buckets[idx] = bucket;
|
||||
}
|
||||
}
|
||||
|
||||
// sort
|
||||
sort_merge(buckets, sizeof(*buckets), bucket_count,
|
||||
distribution_bucket_compare,
|
||||
PtrFromInt(distribution->element_count));
|
||||
|
||||
// reorder buckets
|
||||
distribution->first = 0;
|
||||
distribution->last = 0;
|
||||
for (U32 i = 0; i < bucket_count; i += 1){
|
||||
DISTRIBUTION_Bucket *bucket = buckets[i];
|
||||
bucket->next = 0;
|
||||
SLLQueuePush(distribution->first, distribution->last, bucket);
|
||||
}
|
||||
|
||||
arena_release_scratch(&scratch);
|
||||
}
|
||||
|
||||
function S32
|
||||
distribution_bucket_compare(void *a, void *b, void *udata){
|
||||
U32 element_count = (U32)IntFromPtr(udata);
|
||||
DISTRIBUTION_Bucket *abucket = *(DISTRIBUTION_Bucket**)a;
|
||||
DISTRIBUTION_Bucket *bbucket = *(DISTRIBUTION_Bucket**)b;
|
||||
S32 result = memcmp(abucket->order, bbucket->order, sizeof(*abucket->order)*element_count);
|
||||
return(result);
|
||||
}
|
||||
|
||||
// tests
|
||||
|
||||
function TEST
|
||||
make_test1(Arena *arena){
|
||||
// POSET:
|
||||
// 0 1
|
||||
// / \ / \
|
||||
// 2 3 4
|
||||
//
|
||||
// ORDERS:
|
||||
// 01234
|
||||
// 01243
|
||||
// 01324
|
||||
// 01342
|
||||
// 01423
|
||||
// 01432
|
||||
// 02134
|
||||
// 02143
|
||||
// 10234
|
||||
// 10243
|
||||
// 10324
|
||||
// 10342
|
||||
// 10423
|
||||
// 10432
|
||||
// 14023
|
||||
// 14032
|
||||
|
||||
ArenaTemp scratch = arena_get_scratch(&arena, 1);
|
||||
|
||||
POSET_Loose *poset = poset_new(scratch.arena, 5);
|
||||
poset_order(scratch.arena, poset, 0, 2);
|
||||
poset_order(scratch.arena, poset, 0, 3);
|
||||
poset_order(scratch.arena, poset, 1, 3);
|
||||
poset_order(scratch.arena, poset, 1, 4);
|
||||
|
||||
DISTRIBUTION *distribution = distribution_new(arena, 5);
|
||||
distribution_add(arena, distribution, 1, 0,1,2,3,4);
|
||||
distribution_add(arena, distribution, 1, 0,1,2,4,3);
|
||||
distribution_add(arena, distribution, 1, 0,1,3,2,4);
|
||||
distribution_add(arena, distribution, 1, 0,1,3,4,2);
|
||||
distribution_add(arena, distribution, 1, 0,1,4,2,3);
|
||||
distribution_add(arena, distribution, 1, 0,1,4,3,2);
|
||||
distribution_add(arena, distribution, 1, 0,2,1,3,4);
|
||||
distribution_add(arena, distribution, 1, 0,2,1,4,3);
|
||||
|
||||
distribution_add(arena, distribution, 1, 1,0,2,3,4);
|
||||
distribution_add(arena, distribution, 1, 1,0,2,4,3);
|
||||
distribution_add(arena, distribution, 1, 1,0,3,2,4);
|
||||
distribution_add(arena, distribution, 1, 1,0,3,4,2);
|
||||
distribution_add(arena, distribution, 1, 1,0,4,2,3);
|
||||
distribution_add(arena, distribution, 1, 1,0,4,3,2);
|
||||
distribution_add(arena, distribution, 1, 1,4,0,3,2);
|
||||
distribution_add(arena, distribution, 1, 1,4,0,2,3);
|
||||
|
||||
distribution_normalize(distribution);
|
||||
|
||||
TEST test = {0};
|
||||
test.poset = poset_bake(arena, poset);
|
||||
test.distribution = distribution;
|
||||
|
||||
arena_release_scratch(&scratch);
|
||||
|
||||
return(test);
|
||||
}
|
||||
|
||||
function POSET*
|
||||
make_big_poset(Arena *arena){
|
||||
|
||||
ArenaTemp scratch = arena_get_scratch(&arena, 1);
|
||||
|
||||
POSET_Loose *poset = poset_new(scratch.arena, 10);
|
||||
for (U32 i = 1; i < 10; i += 1){
|
||||
for (U32 j = i + 1; j < 10; j += 1){
|
||||
if ((j%i) == 0){
|
||||
poset_order(arena, poset, i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
POSET *result = poset_bake(arena, poset);
|
||||
|
||||
arena_release_scratch(&scratch);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
int main(void){
|
||||
Arena *arena = arena_alloc();
|
||||
PRNG prng = {0};
|
||||
{
|
||||
PRNG_Seed seed = {0};
|
||||
os_get_entropy(&seed, sizeof(seed));
|
||||
prng = prng_init_from_seed(&seed);
|
||||
}
|
||||
|
||||
#if 1
|
||||
TEST test1 = make_test1(arena);
|
||||
POSET *poset = test1.poset;
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
POSET *poset = make_big_poset(arena);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
{
|
||||
m4_printf("MATRIX:\n");
|
||||
U32 element_count = poset->element_count;;
|
||||
for (U32 row = 0; row < element_count; row += 1){
|
||||
for (U32 col = 0; col < element_count; col += 1){
|
||||
S8 v = poset->compare[row + col*element_count];
|
||||
if (v < 0){
|
||||
m4_printf("-");
|
||||
}
|
||||
else if (v == 0){
|
||||
m4_printf("0");
|
||||
}
|
||||
else if (v > 0){
|
||||
m4_printf("+");
|
||||
}
|
||||
}
|
||||
m4_printf("\n");
|
||||
}
|
||||
m4_printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
{
|
||||
U32 *array = poset_topological_sort(arena, poset);
|
||||
{
|
||||
m4_printf("INITIAL SORT: { ");
|
||||
for (U32 i = 0; i < poset->element_count; i += 1){
|
||||
m4_printf("%u, ", array[i]);
|
||||
}
|
||||
m4_printf("}\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
DISTRIBUTION *test_distribution = distribution_new(arena, poset->element_count);
|
||||
|
||||
Arena *test_arena = arena_alloc();
|
||||
for (U32 r = 0; r < Million(100); r += 1){
|
||||
U32 *array = poset_shuffle(test_arena, &prng, poset);
|
||||
distribution_add_order(arena, test_distribution, 1.f, array);
|
||||
arena_pop_to(test_arena, 0);
|
||||
}
|
||||
|
||||
distribution_normalize(test_distribution);
|
||||
distribution_sort(test_distribution);
|
||||
|
||||
{
|
||||
m4_printf("DISTRIBUTION:\n");
|
||||
for (DISTRIBUTION_Bucket *bucket = test_distribution->first;
|
||||
bucket != 0;
|
||||
bucket = bucket->next){
|
||||
m4_printf("{ ");
|
||||
for (U32 i = 0; i < poset->element_count; i += 1){
|
||||
m4_printf("%u, ", bucket->order[i]);
|
||||
}
|
||||
m4_printf("} - %.5f\n", bucket->weight);
|
||||
}
|
||||
m4_printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
return(0);
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
#ifndef SHUFFLE_POSET_H
|
||||
#define SHUFFLE_POSET_H
|
||||
|
||||
////////////////////////////////
|
||||
// Types
|
||||
|
||||
typedef struct POSET_Pair{
|
||||
struct POSET_Pair *next;
|
||||
U32 ls;
|
||||
U32 gr;
|
||||
} POSET_Pair;
|
||||
|
||||
typedef struct POSET_Loose{
|
||||
POSET_Pair *first;
|
||||
POSET_Pair *last;
|
||||
U64 pair_count;
|
||||
U32 element_count;
|
||||
} POSET_Loose;
|
||||
|
||||
typedef struct POSET{
|
||||
S8 *compare;
|
||||
U32 element_count;
|
||||
} POSET;
|
||||
|
||||
typedef struct DISTRIBUTION_Bucket{
|
||||
struct DISTRIBUTION_Bucket *next;
|
||||
struct DISTRIBUTION_Bucket *next_hash;
|
||||
U32 *order;
|
||||
U32 hash;
|
||||
F32 weight;
|
||||
} DISTRIBUTION_Bucket;
|
||||
|
||||
typedef struct DISTRIBUTION{
|
||||
DISTRIBUTION_Bucket *first;
|
||||
DISTRIBUTION_Bucket *last;
|
||||
DISTRIBUTION_Bucket *buckets[32];
|
||||
U32 element_count;
|
||||
U32 bucket_count;
|
||||
} DISTRIBUTION;
|
||||
|
||||
typedef struct TEST{
|
||||
POSET *poset;
|
||||
DISTRIBUTION *distribution;
|
||||
} TEST;
|
||||
|
||||
////////////////////////////////
|
||||
// Functions
|
||||
|
||||
// poset
|
||||
|
||||
function POSET_Loose* poset_new(Arena *arena, U32 element_count);
|
||||
function void poset_order(Arena *arena, POSET_Loose *poset, U32 ls, U32 gr);
|
||||
function POSET* poset_bake(Arena *arena, POSET_Loose *loose);
|
||||
|
||||
function U32* poset_topological_sort(Arena *arena, POSET *poset);
|
||||
function U32* poset_shuffle(Arena *arena, PRNG *prng, POSET *poset);
|
||||
|
||||
// distribution
|
||||
|
||||
function DISTRIBUTION* distribution_new(Arena *arena, U32 element_count);
|
||||
function void distribution_add(Arena *arena, DISTRIBUTION *distribution, F32 weight, ...);
|
||||
function void distribution_add_order(Arena *arena, DISTRIBUTION *distribution, F32 weight, U32 *order);
|
||||
function void distribution_normalize(DISTRIBUTION *distribution);
|
||||
function void distribution_sort(DISTRIBUTION *distribution);
|
||||
function S32 distribution_bucket_compare(void *a, void *b, void *udata);
|
||||
|
||||
// test cases
|
||||
|
||||
function TEST make_test1(Arena *arena);
|
||||
function POSET*make_big_poset(Arena *arena);
|
||||
|
||||
#endif //SHUFFLE_POSET_H
|
Loading…
Reference in New Issue