#pragma once
#include <Eigen/Core>

namespace V3DD
{
    class Color
    {
    public:
        static Eigen::Vector4d air_force_blue_raf;
        static Eigen::Vector4d air_force_blue_usaf;
        static Eigen::Vector4d air_superiority_blue;
        static Eigen::Vector4d alabama_crimson;
        static Eigen::Vector4d alice_blue;
        static Eigen::Vector4d alizarin_crimson;
        static Eigen::Vector4d alloy_orange;
        static Eigen::Vector4d almond;
        static Eigen::Vector4d amaranth;
        static Eigen::Vector4d amber;
        static Eigen::Vector4d amber_sae_ece;
        static Eigen::Vector4d american_rose;
        static Eigen::Vector4d amethyst;
        static Eigen::Vector4d android_green;
        static Eigen::Vector4d anti_flash_white;
        static Eigen::Vector4d antique_brass;
        static Eigen::Vector4d antique_fuchsia;
        static Eigen::Vector4d antique_ruby;
        static Eigen::Vector4d antique_white;
        static Eigen::Vector4d ao_english;
        static Eigen::Vector4d apple_green;
        static Eigen::Vector4d apricot;
        static Eigen::Vector4d aqua;
        static Eigen::Vector4d aquamarine;
        static Eigen::Vector4d army_green;
        static Eigen::Vector4d arsenic;
        static Eigen::Vector4d arylide_yellow;
        static Eigen::Vector4d ash_grey;
        static Eigen::Vector4d asparagus;
        static Eigen::Vector4d atomic_tangerine;
        static Eigen::Vector4d auburn;
        static Eigen::Vector4d aureolin;
        static Eigen::Vector4d aurometalsaurus;
        static Eigen::Vector4d avocado;
        static Eigen::Vector4d azure;
        static Eigen::Vector4d azure_mist_web;
        static Eigen::Vector4d baby_blue;
        static Eigen::Vector4d baby_blue_eyes;
        static Eigen::Vector4d baby_pink;
        static Eigen::Vector4d ball_blue;
        static Eigen::Vector4d banana_mania;
        static Eigen::Vector4d banana_yellow;
        static Eigen::Vector4d barn_red;
        static Eigen::Vector4d battleship_grey;
        static Eigen::Vector4d bazaar;
        static Eigen::Vector4d beau_blue;
        static Eigen::Vector4d beaver;
        static Eigen::Vector4d beige;
        static Eigen::Vector4d big_dip_o_ruby;
        static Eigen::Vector4d bisque;
        static Eigen::Vector4d bistre;
        static Eigen::Vector4d bittersweet;
        static Eigen::Vector4d bittersweet_shimmer;
        static Eigen::Vector4d black;
        static Eigen::Vector4d black_bean;
        static Eigen::Vector4d black_leather_jacket;
        static Eigen::Vector4d black_olive;
        static Eigen::Vector4d blanched_almond;
        static Eigen::Vector4d blast_off_bronze;
        static Eigen::Vector4d bleu_de_france;
        static Eigen::Vector4d blizzard_blue;
        static Eigen::Vector4d blond;
        static Eigen::Vector4d blue;
        static Eigen::Vector4d blue_bell;
        static Eigen::Vector4d blue_crayola;
        static Eigen::Vector4d blue_gray;
        static Eigen::Vector4d blue_green;
        static Eigen::Vector4d blue_munsell;
        static Eigen::Vector4d blue_ncs;
        static Eigen::Vector4d blue_pigment;
        static Eigen::Vector4d blue_ryb;
        static Eigen::Vector4d blue_sapphire;
        static Eigen::Vector4d blue_violet;
        static Eigen::Vector4d blush;
        static Eigen::Vector4d bole;
        static Eigen::Vector4d bondi_blue;
        static Eigen::Vector4d bone;
        static Eigen::Vector4d boston_university_red;
        static Eigen::Vector4d bottle_green;
        static Eigen::Vector4d boysenberry;
        static Eigen::Vector4d brandeis_blue;
        static Eigen::Vector4d brass;
        static Eigen::Vector4d brick_red;
        static Eigen::Vector4d bright_cerulean;
        static Eigen::Vector4d bright_green;
        static Eigen::Vector4d bright_lavender;
        static Eigen::Vector4d bright_maroon;
        static Eigen::Vector4d bright_pink;
        static Eigen::Vector4d bright_turquoise;
        static Eigen::Vector4d bright_ube;
        static Eigen::Vector4d brilliant_lavender;
        static Eigen::Vector4d brilliant_rose;
        static Eigen::Vector4d brink_pink;
        static Eigen::Vector4d british_racing_green;
        static Eigen::Vector4d bronze;
        static Eigen::Vector4d brown_traditional;
        static Eigen::Vector4d brown_web;
        static Eigen::Vector4d bubble_gum;
        static Eigen::Vector4d bubbles;
        static Eigen::Vector4d buff;
        static Eigen::Vector4d bulgarian_rose;
        static Eigen::Vector4d burgundy;
        static Eigen::Vector4d burlywood;
        static Eigen::Vector4d burnt_orange;
        static Eigen::Vector4d burnt_sienna;
        static Eigen::Vector4d burnt_umber;
        static Eigen::Vector4d byzantine;
        static Eigen::Vector4d byzantium;
        static Eigen::Vector4d cadet;
        static Eigen::Vector4d cadet_blue;
        static Eigen::Vector4d cadet_grey;
        static Eigen::Vector4d cadmium_green;
        static Eigen::Vector4d cadmium_orange;
        static Eigen::Vector4d cadmium_red;
        static Eigen::Vector4d cadmium_yellow;
        static Eigen::Vector4d caf_au_lait;
        static Eigen::Vector4d caf_noir;
        static Eigen::Vector4d cal_poly_green;
        static Eigen::Vector4d cambridge_blue;
        static Eigen::Vector4d camel;
        static Eigen::Vector4d cameo_pink;
        static Eigen::Vector4d camouflage_green;
        static Eigen::Vector4d canary_yellow;
        static Eigen::Vector4d candy_apple_red;
        static Eigen::Vector4d candy_pink;
        static Eigen::Vector4d capri;
        static Eigen::Vector4d caput_mortuum;
        static Eigen::Vector4d cardinal;
        static Eigen::Vector4d caribbean_green;
        static Eigen::Vector4d carmine;
        static Eigen::Vector4d carmine_m_p;
        static Eigen::Vector4d carmine_pink;
        static Eigen::Vector4d carmine_red;
        static Eigen::Vector4d carnation_pink;
        static Eigen::Vector4d carnelian;
        static Eigen::Vector4d carolina_blue;
        static Eigen::Vector4d carrot_orange;
        static Eigen::Vector4d catalina_blue;
        static Eigen::Vector4d ceil;
        static Eigen::Vector4d celadon;
        static Eigen::Vector4d celadon_blue;
        static Eigen::Vector4d celadon_green;
        static Eigen::Vector4d celeste_colour;
        static Eigen::Vector4d celestial_blue;
        static Eigen::Vector4d cerise;
        static Eigen::Vector4d cerise_pink;
        static Eigen::Vector4d cerulean;
        static Eigen::Vector4d cerulean_blue;
        static Eigen::Vector4d cerulean_frost;
        static Eigen::Vector4d cg_blue;
        static Eigen::Vector4d cg_red;
        static Eigen::Vector4d chamoisee;
        static Eigen::Vector4d champagne;
        static Eigen::Vector4d charcoal;
        static Eigen::Vector4d charm_pink;
        static Eigen::Vector4d chartreuse_traditional;
        static Eigen::Vector4d chartreuse_web;
        static Eigen::Vector4d cherry;
        static Eigen::Vector4d cherry_blossom_pink;
        static Eigen::Vector4d chestnut;
        static Eigen::Vector4d china_pink;
        static Eigen::Vector4d china_rose;
        static Eigen::Vector4d chinese_red;
        static Eigen::Vector4d chocolate_traditional;
        static Eigen::Vector4d chocolate_web;
        static Eigen::Vector4d chrome_yellow;
        static Eigen::Vector4d cinereous;
        static Eigen::Vector4d cinnabar;
        static Eigen::Vector4d cinnamon;
        static Eigen::Vector4d citrine;
        static Eigen::Vector4d classic_rose;
        static Eigen::Vector4d cobalt;
        static Eigen::Vector4d cocoa_brown;
        static Eigen::Vector4d coffee;
        static Eigen::Vector4d columbia_blue;
        static Eigen::Vector4d congo_pink;
        static Eigen::Vector4d cool_black;
        static Eigen::Vector4d cool_grey;
        static Eigen::Vector4d copper;
        static Eigen::Vector4d copper_crayola;
        static Eigen::Vector4d copper_penny;
        static Eigen::Vector4d copper_red;
        static Eigen::Vector4d copper_rose;
        static Eigen::Vector4d coquelicot;
        static Eigen::Vector4d coral;
        static Eigen::Vector4d coral_pink;
        static Eigen::Vector4d coral_red;
        static Eigen::Vector4d cordovan;
        static Eigen::Vector4d corn;
        static Eigen::Vector4d cornell_red;
        static Eigen::Vector4d cornflower_blue;
        static Eigen::Vector4d cornsilk;
        static Eigen::Vector4d cosmic_latte;
        static Eigen::Vector4d cotton_candy;
        static Eigen::Vector4d cream;
        static Eigen::Vector4d crimson;
        static Eigen::Vector4d crimson_glory;
        static Eigen::Vector4d cyan;
        static Eigen::Vector4d cyan_process;
        static Eigen::Vector4d daffodil;
        static Eigen::Vector4d dandelion;
        static Eigen::Vector4d dark_blue;
        static Eigen::Vector4d dark_brown;
        static Eigen::Vector4d dark_byzantium;
        static Eigen::Vector4d dark_candy_apple_red;
        static Eigen::Vector4d dark_cerulean;
        static Eigen::Vector4d dark_chestnut;
        static Eigen::Vector4d dark_coral;
        static Eigen::Vector4d dark_cyan;
        static Eigen::Vector4d dark_electric_blue;
        static Eigen::Vector4d dark_goldenrod;
        static Eigen::Vector4d dark_gray;
        static Eigen::Vector4d dark_green;
        static Eigen::Vector4d dark_imperial_blue;
        static Eigen::Vector4d dark_jungle_green;
        static Eigen::Vector4d dark_khaki;
        static Eigen::Vector4d dark_lava;
        static Eigen::Vector4d dark_lavender;
        static Eigen::Vector4d dark_magenta;
        static Eigen::Vector4d dark_midnight_blue;
        static Eigen::Vector4d dark_olive_green;
        static Eigen::Vector4d dark_orange;
        static Eigen::Vector4d dark_orchid;
        static Eigen::Vector4d dark_pastel_blue;
        static Eigen::Vector4d dark_pastel_green;
        static Eigen::Vector4d dark_pastel_purple;
        static Eigen::Vector4d dark_pastel_red;
        static Eigen::Vector4d dark_pink;
        static Eigen::Vector4d dark_powder_blue;
        static Eigen::Vector4d dark_raspberry;
        static Eigen::Vector4d dark_red;
        static Eigen::Vector4d dark_salmon;
        static Eigen::Vector4d dark_scarlet;
        static Eigen::Vector4d dark_sea_green;
        static Eigen::Vector4d dark_sienna;
        static Eigen::Vector4d dark_slate_blue;
        static Eigen::Vector4d dark_slate_gray;
        static Eigen::Vector4d dark_spring_green;
        static Eigen::Vector4d dark_tan;
        static Eigen::Vector4d dark_tangerine;
        static Eigen::Vector4d dark_taupe;
        static Eigen::Vector4d dark_terra_cotta;
        static Eigen::Vector4d dark_turquoise;
        static Eigen::Vector4d dark_violet;
        static Eigen::Vector4d dark_yellow;
        static Eigen::Vector4d dartmouth_green;
        static Eigen::Vector4d davy_s_grey;
        static Eigen::Vector4d debian_red;
        static Eigen::Vector4d deep_carmine;
        static Eigen::Vector4d deep_carmine_pink;
        static Eigen::Vector4d deep_carrot_orange;
        static Eigen::Vector4d deep_cerise;
        static Eigen::Vector4d deep_champagne;
        static Eigen::Vector4d deep_chestnut;
        static Eigen::Vector4d deep_coffee;
        static Eigen::Vector4d deep_fuchsia;
        static Eigen::Vector4d deep_jungle_green;
        static Eigen::Vector4d deep_lilac;
        static Eigen::Vector4d deep_magenta;
        static Eigen::Vector4d deep_peach;
        static Eigen::Vector4d deep_pink;
        static Eigen::Vector4d deep_ruby;
        static Eigen::Vector4d deep_saffron;
        static Eigen::Vector4d deep_sky_blue;
        static Eigen::Vector4d deep_tuscan_red;
        static Eigen::Vector4d denim;
        static Eigen::Vector4d desert;
        static Eigen::Vector4d desert_sand;
        static Eigen::Vector4d dim_gray;
        static Eigen::Vector4d dodger_blue;
        static Eigen::Vector4d dogwood_rose;
        static Eigen::Vector4d dollar_bill;
        static Eigen::Vector4d drab;
        static Eigen::Vector4d duke_blue;
        static Eigen::Vector4d earth_yellow;
        static Eigen::Vector4d ebony;
        static Eigen::Vector4d ecru;
        static Eigen::Vector4d eggplant;
        static Eigen::Vector4d eggshell;
        static Eigen::Vector4d egyptian_blue;
        static Eigen::Vector4d electric_blue;
        static Eigen::Vector4d electric_crimson;
        static Eigen::Vector4d electric_cyan;
        static Eigen::Vector4d electric_green;
        static Eigen::Vector4d electric_indigo;
        static Eigen::Vector4d electric_lavender;
        static Eigen::Vector4d electric_lime;
        static Eigen::Vector4d electric_purple;
        static Eigen::Vector4d electric_ultramarine;
        static Eigen::Vector4d electric_violet;
        static Eigen::Vector4d electric_yellow;
        static Eigen::Vector4d emerald;
        static Eigen::Vector4d english_lavender;
        static Eigen::Vector4d eton_blue;
        static Eigen::Vector4d fallow;
        static Eigen::Vector4d falu_red;
        static Eigen::Vector4d fandango;
        static Eigen::Vector4d fashion_fuchsia;
        static Eigen::Vector4d fawn;
        static Eigen::Vector4d feldgrau;
        static Eigen::Vector4d fern_green;
        static Eigen::Vector4d ferrari_red;
        static Eigen::Vector4d field_drab;
        static Eigen::Vector4d fire_engine_red;
        static Eigen::Vector4d firebrick;
        static Eigen::Vector4d flame;
        static Eigen::Vector4d flamingo_pink;
        static Eigen::Vector4d flavescent;
        static Eigen::Vector4d flax;
        static Eigen::Vector4d floral_white;
        static Eigen::Vector4d fluorescent_orange;
        static Eigen::Vector4d fluorescent_pink;
        static Eigen::Vector4d fluorescent_yellow;
        static Eigen::Vector4d folly;
        static Eigen::Vector4d forest_green_traditional;
        static Eigen::Vector4d forest_green_web;
        static Eigen::Vector4d french_beige;
        static Eigen::Vector4d french_blue;
        static Eigen::Vector4d french_lilac;
        static Eigen::Vector4d french_lime;
        static Eigen::Vector4d french_raspberry;
        static Eigen::Vector4d french_rose;
        static Eigen::Vector4d fuchsia;
        static Eigen::Vector4d fuchsia_crayola;
        static Eigen::Vector4d fuchsia_pink;
        static Eigen::Vector4d fuchsia_rose;
        static Eigen::Vector4d fulvous;
        static Eigen::Vector4d fuzzy_wuzzy;
        static Eigen::Vector4d gainsboro;
        static Eigen::Vector4d gamboge;
        static Eigen::Vector4d ghost_white;
        static Eigen::Vector4d ginger;
        static Eigen::Vector4d glaucous;
        static Eigen::Vector4d glitter;
        static Eigen::Vector4d gold_metallic;
        static Eigen::Vector4d gold_web_golden;
        static Eigen::Vector4d golden_brown;
        static Eigen::Vector4d golden_poppy;
        static Eigen::Vector4d golden_yellow;
        static Eigen::Vector4d goldenrod;
        static Eigen::Vector4d granny_smith_apple;
        static Eigen::Vector4d gray;
        static Eigen::Vector4d gray_asparagus;
        static Eigen::Vector4d gray_html_css_gray;
        static Eigen::Vector4d gray_x11_gray;
        static Eigen::Vector4d green;
        static Eigen::Vector4d green_color_wheel_x11_green;
        static Eigen::Vector4d green_crayola;
        static Eigen::Vector4d green_html_css_green;
        static Eigen::Vector4d green_munsell;
        static Eigen::Vector4d green_ncs;
        static Eigen::Vector4d green_pigment;
        static Eigen::Vector4d green_ryb;
        static Eigen::Vector4d green_yellow;
        static Eigen::Vector4d grullo;
        static Eigen::Vector4d guppie_green;
        static Eigen::Vector4d halay_be;
        static Eigen::Vector4d han_blue;
        static Eigen::Vector4d han_purple;
        static Eigen::Vector4d hansa_yellow;
        static Eigen::Vector4d harlequin;
        static Eigen::Vector4d harvard_crimson;
        static Eigen::Vector4d harvest_gold;
        static Eigen::Vector4d heart_gold;
        static Eigen::Vector4d heliotrope;
        static Eigen::Vector4d hollywood_cerise;
        static Eigen::Vector4d honeydew;
        static Eigen::Vector4d honolulu_blue;
        static Eigen::Vector4d hooker_s_green;
        static Eigen::Vector4d hot_magenta;
        static Eigen::Vector4d hot_pink;
        static Eigen::Vector4d hunter_green;
        static Eigen::Vector4d iceberg;
        static Eigen::Vector4d icterine;
        static Eigen::Vector4d imperial_blue;
        static Eigen::Vector4d inchworm;
        static Eigen::Vector4d india_green;
        static Eigen::Vector4d indian_red;
        static Eigen::Vector4d indian_yellow;
        static Eigen::Vector4d indigo;
        static Eigen::Vector4d indigo_dye;
        static Eigen::Vector4d indigo_web;
        static Eigen::Vector4d international_klein_blue;
        static Eigen::Vector4d international_orange_aerospace;
        static Eigen::Vector4d international_orange_engineering;
        static Eigen::Vector4d international_orange_golden_gate_bridge;
        static Eigen::Vector4d iris;
        static Eigen::Vector4d isabelline;
        static Eigen::Vector4d islamic_green;
        static Eigen::Vector4d ivory;
        static Eigen::Vector4d jade;
        static Eigen::Vector4d jasmine;
        static Eigen::Vector4d jasper;
        static Eigen::Vector4d jazzberry_jam;
        static Eigen::Vector4d jet;
        static Eigen::Vector4d jonquil;
        static Eigen::Vector4d june_bud;
        static Eigen::Vector4d jungle_green;
        static Eigen::Vector4d kelly_green;
        static Eigen::Vector4d kenyan_copper;
        static Eigen::Vector4d khaki_html_css_khaki;
        static Eigen::Vector4d khaki_x11_light_khaki;
        static Eigen::Vector4d ku_crimson;
        static Eigen::Vector4d la_salle_green;
        static Eigen::Vector4d languid_lavender;
        static Eigen::Vector4d lapis_lazuli;
        static Eigen::Vector4d laser_lemon;
        static Eigen::Vector4d laurel_green;
        static Eigen::Vector4d lava;
        static Eigen::Vector4d lavender_blue;
        static Eigen::Vector4d lavender_blush;
        static Eigen::Vector4d lavender_floral;
        static Eigen::Vector4d lavender_gray;
        static Eigen::Vector4d lavender_indigo;
        static Eigen::Vector4d lavender_magenta;
        static Eigen::Vector4d lavender_mist;
        static Eigen::Vector4d lavender_pink;
        static Eigen::Vector4d lavender_purple;
        static Eigen::Vector4d lavender_rose;
        static Eigen::Vector4d lavender_web;
        static Eigen::Vector4d lawn_green;
        static Eigen::Vector4d lemon;
        static Eigen::Vector4d lemon_chiffon;
        static Eigen::Vector4d lemon_lime;
        static Eigen::Vector4d licorice;
        static Eigen::Vector4d light_apricot;
        static Eigen::Vector4d light_blue;
        static Eigen::Vector4d light_brown;
        static Eigen::Vector4d light_carmine_pink;
        static Eigen::Vector4d light_coral;
        static Eigen::Vector4d light_cornflower_blue;
        static Eigen::Vector4d light_crimson;
        static Eigen::Vector4d light_cyan;
        static Eigen::Vector4d light_fuchsia_pink;
        static Eigen::Vector4d light_goldenrod_yellow;
        static Eigen::Vector4d light_gray;
        static Eigen::Vector4d light_green;
        static Eigen::Vector4d light_khaki;
        static Eigen::Vector4d light_pastel_purple;
        static Eigen::Vector4d light_pink;
        static Eigen::Vector4d light_red_ochre;
        static Eigen::Vector4d light_salmon;
        static Eigen::Vector4d light_salmon_pink;
        static Eigen::Vector4d light_sea_green;
        static Eigen::Vector4d light_sky_blue;
        static Eigen::Vector4d light_slate_gray;
        static Eigen::Vector4d light_taupe;
        static Eigen::Vector4d light_thulian_pink;
        static Eigen::Vector4d light_yellow;
        static Eigen::Vector4d lilac;
        static Eigen::Vector4d lime_color_wheel;
        static Eigen::Vector4d lime_green;
        static Eigen::Vector4d lime_web_x11_green;
        static Eigen::Vector4d limerick;
        static Eigen::Vector4d lincoln_green;
        static Eigen::Vector4d linen;
        static Eigen::Vector4d lion;
        static Eigen::Vector4d little_boy_blue;
        static Eigen::Vector4d liver;
        static Eigen::Vector4d lust;
        static Eigen::Vector4d magenta;
        static Eigen::Vector4d magenta_dye;
        static Eigen::Vector4d magenta_process;
        static Eigen::Vector4d magic_mint;
        static Eigen::Vector4d magnolia;
        static Eigen::Vector4d mahogany;
        static Eigen::Vector4d maize;
        static Eigen::Vector4d majorelle_blue;
        static Eigen::Vector4d malachite;
        static Eigen::Vector4d manatee;
        static Eigen::Vector4d mango_tango;
        static Eigen::Vector4d mantis;
        static Eigen::Vector4d mardi_gras;
        static Eigen::Vector4d maroon_crayola;
        static Eigen::Vector4d maroon_html_css;
        static Eigen::Vector4d maroon_x11;
        static Eigen::Vector4d mauve;
        static Eigen::Vector4d mauve_taupe;
        static Eigen::Vector4d mauvelous;
        static Eigen::Vector4d maya_blue;
        static Eigen::Vector4d meat_brown;
        static Eigen::Vector4d medium_aquamarine;
        static Eigen::Vector4d medium_blue;
        static Eigen::Vector4d medium_candy_apple_red;
        static Eigen::Vector4d medium_carmine;
        static Eigen::Vector4d medium_champagne;
        static Eigen::Vector4d medium_electric_blue;
        static Eigen::Vector4d medium_jungle_green;
        static Eigen::Vector4d medium_lavender_magenta;
        static Eigen::Vector4d medium_orchid;
        static Eigen::Vector4d medium_persian_blue;
        static Eigen::Vector4d medium_purple;
        static Eigen::Vector4d medium_red_violet;
        static Eigen::Vector4d medium_ruby;
        static Eigen::Vector4d medium_sea_green;
        static Eigen::Vector4d medium_slate_blue;
        static Eigen::Vector4d medium_spring_bud;
        static Eigen::Vector4d medium_spring_green;
        static Eigen::Vector4d medium_taupe;
        static Eigen::Vector4d medium_turquoise;
        static Eigen::Vector4d medium_tuscan_red;
        static Eigen::Vector4d medium_vermilion;
        static Eigen::Vector4d medium_violet_red;
        static Eigen::Vector4d mellow_apricot;
        static Eigen::Vector4d mellow_yellow;
        static Eigen::Vector4d melon;
        static Eigen::Vector4d midnight_blue;
        static Eigen::Vector4d midnight_green_eagle_green;
        static Eigen::Vector4d mikado_yellow;
        static Eigen::Vector4d mint;
        static Eigen::Vector4d mint_cream;
        static Eigen::Vector4d mint_green;
        static Eigen::Vector4d misty_rose;
        static Eigen::Vector4d moccasin;
        static Eigen::Vector4d mode_beige;
        static Eigen::Vector4d moonstone_blue;
        static Eigen::Vector4d mordant_red_19;
        static Eigen::Vector4d moss_green;
        static Eigen::Vector4d mountain_meadow;
        static Eigen::Vector4d mountbatten_pink;
        static Eigen::Vector4d msu_green;
        static Eigen::Vector4d mulberry;
        static Eigen::Vector4d mustard;
        static Eigen::Vector4d myrtle;
        static Eigen::Vector4d nadeshiko_pink;
        static Eigen::Vector4d napier_green;
        static Eigen::Vector4d naples_yellow;
        static Eigen::Vector4d navajo_white;
        static Eigen::Vector4d navy_blue;
        static Eigen::Vector4d neon_carrot;
        static Eigen::Vector4d neon_fuchsia;
        static Eigen::Vector4d neon_green;
        static Eigen::Vector4d new_york_pink;
        static Eigen::Vector4d non_photo_blue;
        static Eigen::Vector4d north_texas_green;
        static Eigen::Vector4d ocean_boat_blue;
        static Eigen::Vector4d ochre;
        static Eigen::Vector4d office_green;
        static Eigen::Vector4d old_gold;
        static Eigen::Vector4d old_lace;
        static Eigen::Vector4d old_lavender;
        static Eigen::Vector4d old_mauve;
        static Eigen::Vector4d old_rose;
        static Eigen::Vector4d olive;
        static Eigen::Vector4d olive_drab_7;
        static Eigen::Vector4d olive_drab_web_olive_drab_3;
        static Eigen::Vector4d olivine;
        static Eigen::Vector4d onyx;
        static Eigen::Vector4d opera_mauve;
        static Eigen::Vector4d orange_color_wheel;
        static Eigen::Vector4d orange_peel;
        static Eigen::Vector4d orange_red;
        static Eigen::Vector4d orange_ryb;
        static Eigen::Vector4d orange_web_color;
        static Eigen::Vector4d orchid;
        static Eigen::Vector4d otter_brown;
        static Eigen::Vector4d ou_crimson_red;
        static Eigen::Vector4d outer_space;
        static Eigen::Vector4d outrageous_orange;
        static Eigen::Vector4d oxford_blue;
        static Eigen::Vector4d pakistan_green;
        static Eigen::Vector4d palatinate_blue;
        static Eigen::Vector4d palatinate_purple;
        static Eigen::Vector4d pale_aqua;
        static Eigen::Vector4d pale_blue;
        static Eigen::Vector4d pale_brown;
        static Eigen::Vector4d pale_carmine;
        static Eigen::Vector4d pale_cerulean;
        static Eigen::Vector4d pale_chestnut;
        static Eigen::Vector4d pale_copper;
        static Eigen::Vector4d pale_cornflower_blue;
        static Eigen::Vector4d pale_gold;
        static Eigen::Vector4d pale_goldenrod;
        static Eigen::Vector4d pale_green;
        static Eigen::Vector4d pale_lavender;
        static Eigen::Vector4d pale_magenta;
        static Eigen::Vector4d pale_pink;
        static Eigen::Vector4d pale_plum;
        static Eigen::Vector4d pale_red_violet;
        static Eigen::Vector4d pale_robin_egg_blue;
        static Eigen::Vector4d pale_silver;
        static Eigen::Vector4d pale_spring_bud;
        static Eigen::Vector4d pale_taupe;
        static Eigen::Vector4d pale_violet_red;
        static Eigen::Vector4d pansy_purple;
        static Eigen::Vector4d papaya_whip;
        static Eigen::Vector4d paris_green;
        static Eigen::Vector4d pastel_blue;
        static Eigen::Vector4d pastel_brown;
        static Eigen::Vector4d pastel_gray;
        static Eigen::Vector4d pastel_green;
        static Eigen::Vector4d pastel_magenta;
        static Eigen::Vector4d pastel_orange;
        static Eigen::Vector4d pastel_pink;
        static Eigen::Vector4d pastel_purple;
        static Eigen::Vector4d pastel_red;
        static Eigen::Vector4d pastel_violet;
        static Eigen::Vector4d pastel_yellow;
        static Eigen::Vector4d patriarch;
        static Eigen::Vector4d payne_s_grey;
        static Eigen::Vector4d peach;
        static Eigen::Vector4d peach_crayola;
        static Eigen::Vector4d peach_orange;
        static Eigen::Vector4d peach_puff;
        static Eigen::Vector4d peach_yellow;
        static Eigen::Vector4d pear;
        static Eigen::Vector4d pearl;
        static Eigen::Vector4d pearl_aqua;
        static Eigen::Vector4d pearly_purple;
        static Eigen::Vector4d peridot;
        static Eigen::Vector4d periwinkle;
        static Eigen::Vector4d persian_blue;
        static Eigen::Vector4d persian_green;
        static Eigen::Vector4d persian_indigo;
        static Eigen::Vector4d persian_orange;
        static Eigen::Vector4d persian_pink;
        static Eigen::Vector4d persian_plum;
        static Eigen::Vector4d persian_red;
        static Eigen::Vector4d persian_rose;
        static Eigen::Vector4d persimmon;
        static Eigen::Vector4d peru;
        static Eigen::Vector4d phlox;
        static Eigen::Vector4d phthalo_blue;
        static Eigen::Vector4d phthalo_green;
        static Eigen::Vector4d piggy_pink;
        static Eigen::Vector4d pine_green;
        static Eigen::Vector4d pink;
        static Eigen::Vector4d pink_lace;
        static Eigen::Vector4d pink_orange;
        static Eigen::Vector4d pink_pearl;
        static Eigen::Vector4d pink_sherbet;
        static Eigen::Vector4d pistachio;
        static Eigen::Vector4d platinum;
        static Eigen::Vector4d plum_traditional;
        static Eigen::Vector4d plum_web;
        static Eigen::Vector4d portland_orange;
        static Eigen::Vector4d powder_blue_web;
        static Eigen::Vector4d princeton_orange;
        static Eigen::Vector4d prune;
        static Eigen::Vector4d prussian_blue;
        static Eigen::Vector4d psychedelic_purple;
        static Eigen::Vector4d puce;
        static Eigen::Vector4d pumpkin;
        static Eigen::Vector4d purple_heart;
        static Eigen::Vector4d purple_html_css;
        static Eigen::Vector4d purple_mountain_majesty;
        static Eigen::Vector4d purple_munsell;
        static Eigen::Vector4d purple_pizzazz;
        static Eigen::Vector4d purple_taupe;
        static Eigen::Vector4d purple_x11;
        static Eigen::Vector4d quartz;
        static Eigen::Vector4d rackley;
        static Eigen::Vector4d radical_red;
        static Eigen::Vector4d rajah;
        static Eigen::Vector4d raspberry;
        static Eigen::Vector4d raspberry_glace;
        static Eigen::Vector4d raspberry_pink;
        static Eigen::Vector4d raspberry_rose;
        static Eigen::Vector4d raw_umber;
        static Eigen::Vector4d razzle_dazzle_rose;
        static Eigen::Vector4d razzmatazz;
        static Eigen::Vector4d red;
        static Eigen::Vector4d red_brown;
        static Eigen::Vector4d red_devil;
        static Eigen::Vector4d red_munsell;
        static Eigen::Vector4d red_ncs;
        static Eigen::Vector4d red_orange;
        static Eigen::Vector4d red_pigment;
        static Eigen::Vector4d red_ryb;
        static Eigen::Vector4d red_violet;
        static Eigen::Vector4d redwood;
        static Eigen::Vector4d regalia;
        static Eigen::Vector4d resolution_blue;
        static Eigen::Vector4d rich_black;
        static Eigen::Vector4d rich_brilliant_lavender;
        static Eigen::Vector4d rich_carmine;
        static Eigen::Vector4d rich_electric_blue;
        static Eigen::Vector4d rich_lavender;
        static Eigen::Vector4d rich_lilac;
        static Eigen::Vector4d rich_maroon;
        static Eigen::Vector4d rifle_green;
        static Eigen::Vector4d robin_egg_blue;
        static Eigen::Vector4d rose;
        static Eigen::Vector4d rose_bonbon;
        static Eigen::Vector4d rose_ebony;
        static Eigen::Vector4d rose_gold;
        static Eigen::Vector4d rose_madder;
        static Eigen::Vector4d rose_pink;
        static Eigen::Vector4d rose_quartz;
        static Eigen::Vector4d rose_taupe;
        static Eigen::Vector4d rose_vale;
        static Eigen::Vector4d rosewood;
        static Eigen::Vector4d rosso_corsa;
        static Eigen::Vector4d rosy_brown;
        static Eigen::Vector4d royal_azure;
        static Eigen::Vector4d royal_blue_traditional;
        static Eigen::Vector4d royal_blue_web;
        static Eigen::Vector4d royal_fuchsia;
        static Eigen::Vector4d royal_purple;
        static Eigen::Vector4d royal_yellow;
        static Eigen::Vector4d rubine_red;
        static Eigen::Vector4d ruby;
        static Eigen::Vector4d ruby_red;
        static Eigen::Vector4d ruddy;
        static Eigen::Vector4d ruddy_brown;
        static Eigen::Vector4d ruddy_pink;
        static Eigen::Vector4d rufous;
        static Eigen::Vector4d russet;
        static Eigen::Vector4d rust;
        static Eigen::Vector4d rusty_red;
        static Eigen::Vector4d sacramento_state_green;
        static Eigen::Vector4d saddle_brown;
        static Eigen::Vector4d safety_orange_blaze_orange;
        static Eigen::Vector4d saffron;
        static Eigen::Vector4d salmon;
        static Eigen::Vector4d salmon_pink;
        static Eigen::Vector4d sand;
        static Eigen::Vector4d sand_dune;
        static Eigen::Vector4d sandstorm;
        static Eigen::Vector4d sandy_brown;
        static Eigen::Vector4d sandy_taupe;
        static Eigen::Vector4d sangria;
        static Eigen::Vector4d sap_green;
        static Eigen::Vector4d sapphire;
        static Eigen::Vector4d sapphire_blue;
        static Eigen::Vector4d satin_sheen_gold;
        static Eigen::Vector4d scarlet;
        static Eigen::Vector4d scarlet_crayola;
        static Eigen::Vector4d school_bus_yellow;
        static Eigen::Vector4d screamin_green;
        static Eigen::Vector4d sea_blue;
        static Eigen::Vector4d sea_green;
        static Eigen::Vector4d seal_brown;
        static Eigen::Vector4d seashell;
        static Eigen::Vector4d selective_yellow;
        static Eigen::Vector4d sepia;
        static Eigen::Vector4d shadow;
        static Eigen::Vector4d shamrock_green;
        static Eigen::Vector4d shocking_pink;
        static Eigen::Vector4d shocking_pink_crayola;
        static Eigen::Vector4d sienna;
        static Eigen::Vector4d silver;
        static Eigen::Vector4d sinopia;
        static Eigen::Vector4d skobeloff;
        static Eigen::Vector4d sky_blue;
        static Eigen::Vector4d sky_magenta;
        static Eigen::Vector4d slate_blue;
        static Eigen::Vector4d slate_gray;
        static Eigen::Vector4d smalt_dark_powder_blue;
        static Eigen::Vector4d smokey_topaz;
        static Eigen::Vector4d smoky_black;
        static Eigen::Vector4d snow;
        static Eigen::Vector4d spiro_disco_ball;
        static Eigen::Vector4d spring_bud;
        static Eigen::Vector4d spring_green;
        static Eigen::Vector4d st_patrick_s_blue;
        static Eigen::Vector4d steel_blue;
        static Eigen::Vector4d stil_de_grain_yellow;
        static Eigen::Vector4d stizza;
        static Eigen::Vector4d stormcloud;
        static Eigen::Vector4d straw;
        static Eigen::Vector4d sunglow;
        static Eigen::Vector4d sunset;
        static Eigen::Vector4d tan;
        static Eigen::Vector4d tangelo;
        static Eigen::Vector4d tangerine;
        static Eigen::Vector4d tangerine_yellow;
        static Eigen::Vector4d tango_pink;
        static Eigen::Vector4d taupe;
        static Eigen::Vector4d taupe_gray;
        static Eigen::Vector4d tea_green;
        static Eigen::Vector4d tea_rose_orange;
        static Eigen::Vector4d tea_rose_rose;
        static Eigen::Vector4d teal;
        static Eigen::Vector4d teal_blue;
        static Eigen::Vector4d teal_green;
        static Eigen::Vector4d telemagenta;
        static Eigen::Vector4d tenn_tawny;
        static Eigen::Vector4d terra_cotta;
        static Eigen::Vector4d thistle;
        static Eigen::Vector4d thulian_pink;
        static Eigen::Vector4d tickle_me_pink;
        static Eigen::Vector4d tiffany_blue;
        static Eigen::Vector4d tiger_s_eye;
        static Eigen::Vector4d timberwolf;
        static Eigen::Vector4d titanium_yellow;
        static Eigen::Vector4d tomato;
        static Eigen::Vector4d toolbox;
        static Eigen::Vector4d topaz;
        static Eigen::Vector4d tractor_red;
        static Eigen::Vector4d trolley_grey;
        static Eigen::Vector4d tropical_rain_forest;
        static Eigen::Vector4d true_blue;
        static Eigen::Vector4d tufts_blue;
        static Eigen::Vector4d tumbleweed;
        static Eigen::Vector4d turkish_rose;
        static Eigen::Vector4d turquoise;
        static Eigen::Vector4d turquoise_blue;
        static Eigen::Vector4d turquoise_green;
        static Eigen::Vector4d tuscan_red;
        static Eigen::Vector4d twilight_lavender;
        static Eigen::Vector4d tyrian_purple;
        static Eigen::Vector4d ua_blue;
        static Eigen::Vector4d ua_red;
        static Eigen::Vector4d ube;
        static Eigen::Vector4d ucla_blue;
        static Eigen::Vector4d ucla_gold;
        static Eigen::Vector4d ufo_green;
        static Eigen::Vector4d ultra_pink;
        static Eigen::Vector4d ultramarine;
        static Eigen::Vector4d ultramarine_blue;
        static Eigen::Vector4d umber;
        static Eigen::Vector4d unbleached_silk;
        static Eigen::Vector4d united_nations_blue;
        static Eigen::Vector4d university_of_california_gold;
        static Eigen::Vector4d unmellow_yellow;
        static Eigen::Vector4d up_forest_green;
        static Eigen::Vector4d up_maroon;
        static Eigen::Vector4d upsdell_red;
        static Eigen::Vector4d urobilin;
        static Eigen::Vector4d usafa_blue;
        static Eigen::Vector4d usc_cardinal;
        static Eigen::Vector4d usc_gold;
        static Eigen::Vector4d utah_crimson;
        static Eigen::Vector4d vanilla;
        static Eigen::Vector4d vegas_gold;
        static Eigen::Vector4d venetian_red;
        static Eigen::Vector4d verdigris;
        static Eigen::Vector4d vermilion_cinnabar;
        static Eigen::Vector4d vermilion_plochere;
        static Eigen::Vector4d veronica;
        static Eigen::Vector4d violet;
        static Eigen::Vector4d violet_blue;
        static Eigen::Vector4d violet_color_wheel;
        static Eigen::Vector4d violet_ryb;
        static Eigen::Vector4d violet_web;
        static Eigen::Vector4d viridian;
        static Eigen::Vector4d vivid_auburn;
        static Eigen::Vector4d vivid_burgundy;
        static Eigen::Vector4d vivid_cerise;
        static Eigen::Vector4d vivid_tangerine;
        static Eigen::Vector4d vivid_violet;
        static Eigen::Vector4d warm_black;
        static Eigen::Vector4d waterspout;
        static Eigen::Vector4d wenge;
        static Eigen::Vector4d wheat;
        static Eigen::Vector4d white;
        static Eigen::Vector4d white_smoke;
        static Eigen::Vector4d wild_blue_yonder;
        static Eigen::Vector4d wild_strawberry;
        static Eigen::Vector4d wild_watermelon;
        static Eigen::Vector4d wine;
        static Eigen::Vector4d wine_dregs;
        static Eigen::Vector4d wisteria;
        static Eigen::Vector4d wood_brown;
        static Eigen::Vector4d xanadu;
        static Eigen::Vector4d yale_blue;
        static Eigen::Vector4d yellow;
        static Eigen::Vector4d yellow_green;
        static Eigen::Vector4d yellow_munsell;
        static Eigen::Vector4d yellow_ncs;
        static Eigen::Vector4d yellow_orange;
        static Eigen::Vector4d yellow_process;
        static Eigen::Vector4d yellow_ryb;
        static Eigen::Vector4d zaffre;
    };
}


#pragma once

#include <vector>
#include <complex>
#include <boost/multi_array.hpp>
#include <cufft.h>
#include <cufftXt.h>
#include "../common/utilities_cuda.cuh"

namespace DNLS {

	std::vector<double> dispersion();

	boost::multi_array<std::complex<double>, 2> evolve_linear_table();

	completion evolve_nonlinear(double beta_dt_symplectic, cudaStream_t stream);

	completion evolve_linear(const cufftDoubleComplex *evolve_linear_table, cudaStream_t stream);

	completion make_linenergies(const cufftDoubleComplex *psis_k, const double *omega, cudaStream_t stream);

	namespace callback {
		extern __constant__ uint16_t chainlen;
		extern __constant__ uint8_t evolve_linear_table_idx;
		extern __constant__ double beta_dt_symplectic;
		extern __device__ const cufftCallbackLoadZ evolve_linear_ptr, evolve_nonlinear_ptr;
	}

}


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
// Copyright (c) 2009-2012 Mateusz Loskot, London, UK.

// This file was modified by Oracle on 2018-2020.
// Modifications copyright (c) 2018-2020 Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_STRATEGIES_AGNOSTIC_POINT_IN_BOX_BY_SIDE_HPP
#define BOOST_GEOMETRY_STRATEGIES_AGNOSTIC_POINT_IN_BOX_BY_SIDE_HPP

#include <boost/array.hpp>
#include <boost/core/ignore_unused.hpp>

#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/coordinate_dimension.hpp>

#include <boost/geometry/algorithms/assign.hpp>

#include <boost/geometry/strategies/side.hpp>

#include <boost/geometry/strategies/covered_by.hpp>
#include <boost/geometry/strategies/within.hpp>
#include <boost/geometry/strategies/geographic/side.hpp>
#include <boost/geometry/strategies/spherical/ssf.hpp>

namespace boost { namespace geometry { namespace strategy
{

namespace within
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail
{

struct decide_within
{
    static inline bool apply(int side, bool& result)
    {
        if (side != 1)
        {
            result = false;
            return false;
        }
        return true; // continue
    }
};

struct decide_covered_by
{
    static inline bool apply(int side, bool& result)
    {
        if (side != 1)
        {
            result = side >= 0;
            return false;
        }
        return true; // continue
    }
};

// WARNING
// This strategy is not suitable for boxes in non-cartesian CSes having edges
// longer than 180deg because e.g. the SSF formula picks the side of the closer
// longitude, so for long edges the side is the opposite.
// Actually it is not suitable for shorter edges either because the edges of
// boxes are defined by meridians and parallels, not great circles or geodesics.
template <typename Decide, typename Point, typename Box, typename Strategy>
inline bool point_in_box_by_side(Point const& point, Box const& box,
                                 Strategy const& strategy)
{
    boost::ignore_unused(strategy);

    // Create (counterclockwise) array of points, the fifth one closes it
    // Every point should be on the LEFT side (=1), or ON the border (=0),
    // So >= 1 or >= 0
    boost::array<typename point_type<Box>::type, 5> bp;
    geometry::detail::assign_box_corners_oriented<true>(box, bp);
    bp[4] = bp[0];

    bool result = true;
    for (int i = 1; i < 5; i++)
    {
        int const side = strategy.apply(point, bp[i - 1], bp[i]);
        if (! Decide::apply(side, result))
        {
            return result;
        }
    }

    return result;
}

} // namespace detail
#endif // DOXYGEN_NO_DETAIL

// There should probably be another category of geometry different than box,
// e.g. rectangle or convex_ring. This strategy should probably be an
// algorithm calling side strategy.

template <typename CalculationType = void>
struct cartesian_point_box_by_side
{
    template <typename Point, typename Box>
    static inline bool apply(Point const& point, Box const& box)
    {
        using side_strategy_type
            = typename strategy::side::services::default_strategy
                <cartesian_tag, CalculationType>::type;

        return within::detail::point_in_box_by_side
            <
                within::detail::decide_within
            >(point, box, side_strategy_type());
    }
};

template <typename CalculationType = void>
struct spherical_point_box_by_side
{
    template <typename Point, typename Box>
    static inline bool apply(Point const& point, Box const& box)
    {
        return within::detail::point_in_box_by_side
            <
                within::detail::decide_within
            >(point, box,
              strategy::side::spherical_side_formula<CalculationType>());
    }
};

template
<
    typename FormulaPolicy = strategy::andoyer,
    typename Spheroid = srs::spheroid<double>,
    typename CalculationType = void
>
struct geographic_point_box_by_side
{
    geographic_point_box_by_side() = default;

    explicit geographic_point_box_by_side(Spheroid const& spheroid)
        : m_side(spheroid)
    {}

    template <typename Point, typename Box>
    bool apply(Point const& point, Box const& box) const
    {
        return within::detail::point_in_box_by_side
            <
                within::detail::decide_within
            >(point, box, m_side);
    }

    Spheroid const& model() const
    {
        return m_side.model();
    }

private:
    strategy::side::geographic
        <
            FormulaPolicy, Spheroid, CalculationType
        > m_side;
};

} // namespace within

namespace covered_by
{

template <typename CalculationType = void>
struct cartesian_point_box_by_side
{
    template <typename Point, typename Box>
    static bool apply(Point const& point, Box const& box)
    {
        using side_strategy_type
            = typename strategy::side::services::default_strategy
                <cartesian_tag, CalculationType>::type;
        return within::detail::point_in_box_by_side
            <
                within::detail::decide_covered_by
            >(point, box, side_strategy_type());
    }
};

template <typename CalculationType = void>
struct spherical_point_box_by_side
{
    template <typename Point, typename Box>
    static bool apply(Point const& point, Box const& box)
    {
        return within::detail::point_in_box_by_side
            <
                within::detail::decide_covered_by
            >(point, box,
              strategy::side::spherical_side_formula<CalculationType>());
    }
};

template
<
    typename FormulaPolicy = strategy::andoyer,
    typename Spheroid = srs::spheroid<double>,
    typename CalculationType = void
>
struct geographic_point_box_by_side
{
    geographic_point_box_by_side() = default;

    explicit geographic_point_box_by_side(Spheroid const& spheroid)
        : m_side(spheroid)
    {}

    template <typename Point, typename Box>
    bool apply(Point const& point, Box const& box) const
    {
        return within::detail::point_in_box_by_side
            <
                within::detail::decide_covered_by
            >(point, box, m_side);
    }

    Spheroid const& model() const
    {
        return m_side.model();
    }

private:
    strategy::side::geographic
        <
            FormulaPolicy, Spheroid, CalculationType
        > m_side;
};

}

}}} // namespace boost::geometry::strategy

#endif // BOOST_GEOMETRY_STRATEGIES_AGNOSTIC_POINT_IN_BOX_BY_SIDE_HPP


/*******************************************************************************
*
*  Filename    : KeysCompMgr.cc
*  Description : Definition of how objects are stored and data is loaded
*  Author      : Yi-Mu "Enoch" Chen [ ensc@hep1.phys.ntu.edu.tw ]
*
*******************************************************************************/
#include "TstarAnalysis/MassRecoCompare/interface/KeysCompMgr.hpp"

#include "TstarAnalysis/Common/interface/GetEventWeight.hpp"
#include "TstarAnalysis/MassRecoCompare/interface/Common.hpp"

#include "RooAddPdf.h"
#include "RooBifurGauss.h"
#include "RooConstVar.h"
#include "RooDataSet.h"
#include "RooFFTConvPdf.h"
#include "RooGaussian.h"
#include "RooKeysPdf.h"
#include "RooRealVar.h"
#include "TFile.h"

#include <boost/format.hpp>
#include <iostream>

using namespace std;
using namespace mgr;

/*******************************************************************************
*   Declaring global RooRealVars
*******************************************************************************/
void
KeysCompMgr::InitStaticVars()
{
  StaticNewVar( "x", "m_{t+jet}", "GeV/c^{2}", 400, 2500 );
  StaticNewVar( "w", "w",         "",        -1000, 1000 );
  StaticNewVar( "n", "n",         "",           -1,    7 );
}

/******************************************************************************/

RooRealVar&
KeysCompMgr::x(){ return *StaticVar( "x" ); }

/******************************************************************************/

RooRealVar&
KeysCompMgr::w(){ return *StaticVar( "w" ); }

/******************************************************************************/

RooRealVar&
KeysCompMgr::n(){ return *StaticVar( "n" );}

/*******************************************************************************
*   Constructor and data set definition
*******************************************************************************/
KeysCompMgr::KeysCompMgr( const std::string& name, const std::string& latexname ) :
  Named( name ),
  RooFitMgr( name )
{
  SetLatexName( latexname );
  RooDataSet* set = new RooDataSet(
    "",
    "",
    RooArgSet( x(), n(), w() ),
    RooFit::WeightVar( w() )
    );
  AddDataSet( set );
}

/******************************************************************************/

KeysCompMgr::~KeysCompMgr()
{
  for( const auto& name : VarNameList() ){
    RooRealVar* var = Var( name );
    cout << boost::format( "%-50s %8.4lf %8.4f %8.4f" )
      % var->GetName()
      % var->getVal()
      % var->getErrorHi()
      % var->getErrorLo()
         << endl;
  }

  for( const auto& name : FuncNameList() ){
    RooAbsReal* func = Func( name );
    cout << boost::format( "%50s %8.1lf" )
    % func->GetName()
    %func->getVal()
    << endl;
  }
}

/*******************************************************************************
*   Filling by event looping
*******************************************************************************/
void
KeysCompMgr::FillDataSet( const std::string& filename )
{
  fwlite::Event evt( TFile::Open( filename.c_str() ) );
  fwlite::Handle<RecoResult> resulthandle;

  for( evt.toBegin(); !evt.atEnd(); ++evt ){
    resulthandle.getByLabel( evt, Name().c_str(), LabelName( Name() ).c_str(), ProcessName().c_str() );

    const double evtweight = GetEventWeight( evt );
    const double tstarmass = resulthandle.ref().TstarMass();

    if( tstarmass > x().getMax() ){ continue; }
    if( tstarmass < x().getMin() ){ continue; }
    if( evtweight > w().getMax() ){ continue; }
    if( evtweight < w().getMin() ){ continue; }
    x() = tstarmass;
    n() = (double)NumCorrectAssign( resulthandle.ref() );
    DataSet( "" )->add( RooArgSet( x(), n() ), evtweight );
  }

  cout << "Done!" << endl;
}

/******************************************************************************/

void
KeysCompMgr::MakeKeysPdf()
{
  RooDataSet& dataset = *( dynamic_cast<RooDataSet*>( DataSet( "" ) ) );

  double na = dataset.sigma( x() );
  double n2 = dataset.sigma( x(), "n>=3"  );
  double n3 = dataset.sigma( x(), "n>=4"  );
  double n4 = dataset.sigma( x(), "n>=5"  );

  static const vector<double> rholist = {
    1.364,
    1.166,
    1.660
  };
  cout << "Computed Value: "
       << sqrt( na/n2 ) << " "
       << sqrt( na/n3 ) << " "
       << sqrt( na/n4 ) << " "
       << na << endl;

  RooKeysPdf* pdf;

  boost::format nameformat( "rho%.2lf" );
  boost::format titleformat( "#rho = %.2lf" );

  for( const auto rho : rholist ){
    const string name  = str( nameformat % rho );
    const string title = str( titleformat % rho );
    pdf = new RooKeysPdf(
      name.c_str(),
      title.c_str(),
      x(),
      dataset,
      RooKeysPdf::NoMirror,
      rho
      );
    AddPdf( pdf );
  }
}

/******************************************************************************/

void
KeysCompMgr::MakeGauss()
{
  RooRealVar* m  = NewVar( "m", reconamer.GetInput<int>( "mass" ), 400, 3000 );
  RooRealVar* s  = NewVar( "s", 200, 0, 1000 );
  RooAbsPdf* pdf = new RooGaussian( "gauss", "", x(), *m, *s );
  AddPdf( pdf );
  pdf->fitTo(
    *DataSet( "" ),
    RooFit::Minimizer( "Minuit", "Migrad" ),
    RooFit::Minos( kTRUE ),
    RooFit::SumW2Error( kTRUE ),
    RooFit::Verbose( kFALSE ),
    RooFit::PrintLevel( -1 ),
    RooFit::PrintEvalErrors( -1 ),
    RooFit::Warnings( kFALSE )
    );

  RooRealVar* m1         = NewVar( "m1", reconamer.GetInput<int>( "mass" ), 400, 3000 );
  RooRealVar* m2         = NewVar( "m2", reconamer.GetInput<int>( "mass" )+200, 400, 3000 );
  RooRealVar* s1         = NewVar( "s1", 200, 0, 1000 );
  RooRealVar* s2         = NewVar( "s2", 200, 0, 1000 );
  RooRealVar* frac       = NewVar( "frac", 0.5, 0, 1 );
  RooAbsPdf* gauss1      = new RooGaussian( "guass2_1", "", x(), *m1, *s1 );
  RooAbsPdf* gauss2      = new RooGaussian( "guass2_2", "", x(), *m2, *s2 );
  RooAbsPdf* doublegauss = new RooAddPdf( "doublegauss", "", *gauss1, *gauss2, *frac );
  AddPdf( doublegauss );
  doublegauss->fitTo(
    *DataSet( "" ),
    RooFit::Minimizer( "Minuit", "Migrad" ),
    RooFit::Minos( kTRUE ),
    RooFit::SumW2Error( kTRUE ),
    RooFit::Verbose( kFALSE ),
    RooFit::PrintLevel( -1 ),
    RooFit::PrintEvalErrors( -1 ),
    RooFit::Warnings( kFALSE )
    );

}

void
KeysCompMgr::MakeConv()
{
  // Single bifurcation
  RooRealVar* bimean = NewVar( "bimean", 1200, 700, 2000 );
  RooRealVar* bil    = NewVar( "bil", 200, 0, 1000 );
  RooRealVar* bir    = NewVar( "bir", 300, 0, 1000 );
  RooAbsPdf* bipdf   = new RooBifurGauss( "bigauss", "", x(), *bimean, *bil, *bir );

  RooConstVar* gmean = new RooConstVar( "convgmean", "", 0 );
  RooRealVar* gsig   = NewVar( "convgsig", 100, 0, 1000 );
  RooAbsPdf* gpdf    = new RooGaussian( "convgauss", "", x(), *gmean, *gsig );

  RooAbsPdf* convpdf = new RooFFTConvPdf( "conv", "conv", x(), *bipdf, *gpdf );

  convpdf->fitTo(// Forcing fit, and setting to constant
    *DataSet( "" ),
    RooFit::Minimizer( "Minuit", "Migrad" ),
    RooFit::Minos( kTRUE ),
    RooFit::SumW2Error( kTRUE ),
    RooFit::Verbose( kFALSE ),
    RooFit::PrintLevel( -1 ),
    RooFit::PrintEvalErrors( -1 ),
    RooFit::Warnings( kFALSE )
    );
  AddPdf( bipdf );
  AddPdf( gpdf );
  AddPdf( convpdf );
  AddFunc( gmean );

  // Double bifurcation
  RooRealVar* bimean1 = NewVar( "bimean1", 1000, 400, 2000 );
  RooRealVar* bil1    = NewVar( "bil1", 200, 0, 1000 );
  RooRealVar* bir1    = NewVar( "bir1", 300, 0, 1000 );
  RooAbsPdf* bipdf1   = new RooBifurGauss( "bigauss1", "", x(), *bimean1, *bil1, *bir1 );

  RooConstVar* gmean1 = new RooConstVar( "convgmean1", "", 0 );
  RooRealVar* gsig1   = NewVar( "convgsig1", 100, 0, 1000 );
  RooAbsPdf* gpdf1    = new RooGaussian( "convgauss1", "", x(), *gmean1, *gsig1 );

  RooAbsPdf* conv1pdf = new RooFFTConvPdf( "conv1", "", x(), *bipdf1, *gpdf1 );

  RooRealVar* bimean2 = NewVar( "bimean2", 1600, 400, 2000 );
  RooRealVar* bil2    = NewVar( "bil2", 100, 0, 1000 );
  RooRealVar* bir2    = NewVar( "bir2", 200, 0, 1000 );
  RooAbsPdf* bipdf2   = new RooBifurGauss( "bigauss2", "", x(), *bimean2, *bil2, *bir2 );

  RooConstVar* gmean2 = new RooConstVar( "convgmean2", "", 0 );
  RooRealVar* gsig2   = NewVar( "convgsig2", 100, 0, 1000 );
  RooAbsPdf* gpdf2    = new RooGaussian( "convgauss2", "", x(), *gmean2, *gsig2 );

  RooAbsPdf* conv2pdf = new RooFFTConvPdf( "conv2", "", x(), *bipdf2, *gpdf2 );

  RooRealVar* bico = NewVar( "bicoeff2", 0.6, 0, 1 );

  RooAbsPdf* bisum = new RooAddPdf( "convsum2", "", *conv1pdf, *conv2pdf, *bico );

  bisum->fitTo(// Forcing fit, and setting to constant
    *DataSet( "" ),
    RooFit::Minimizer( "Minuit", "Migrad" ),
    RooFit::Minos( kTRUE ),
    RooFit::SumW2Error( kTRUE ),
    RooFit::Verbose( kFALSE ),
    RooFit::PrintLevel( -1 ),
    RooFit::PrintEvalErrors( -1 ),
    RooFit::Warnings( kFALSE )
    );

  // make sample claim ownership of pdfs

  AddPdf( bipdf1 );
  AddPdf( gpdf1 );
  AddPdf( conv1pdf );
  AddPdf( bipdf2 );
  AddPdf( gpdf2 );
  AddPdf( conv2pdf );
  AddPdf( bisum );

  AddFunc( gmean1 );
  AddFunc( gmean2 );
}


#include <iostream>
#include <thread>
#include <mutex>
#include <cstring>
#include <cerrno>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

using std::cout;
using std::endl;
using std::string;

using boost::property_tree::ptree;
using boost::property_tree::read_json;
using boost::property_tree::write_json;

#include "net.h"

class ball_physics
{
public:
    ball_physics(server_t& server) : server(server) {}

    ~ball_physics()
    {
        physics_thread.join();
    }

    void start()
    {
        game.ball.pos.z = 5.0f;
        game.ball.vec.z = game.start_player ? 2.0f : -2.0f;

        std::ostringstream out_data_str;
        ptree out_data_tree;
        out_data_tree.put("obj", "ball");
        out_data_tree.put("px", game.ball.pos.x);
        out_data_tree.put("py", game.ball.pos.y);
        out_data_tree.put("pz", game.ball.pos.z);
        write_json(out_data_str, out_data_tree);
        server.send_all(out_data_str.str());

        physics_thread = std::thread{ &ball_physics::update, this };
    }

    void update()
    {
        game_t::ball_t& ball = game.ball;

        constexpr float delta = 1.0f/60;
        constexpr float ball_radius = 0.1f;
        constexpr float paddle_size = 0.6f;
        constexpr float speed_up = 1.1f;
        constexpr float acc_factor = 5.0f;
        constexpr float aspect_ratio = 720.0f / 1280;

        while (true) {
            std::this_thread::sleep_for(std::chrono::microseconds((uint64_t) (delta * 1000000)));

            ball.pos.x += ball.vec.x * delta;
            ball.pos.y += ball.vec.y * delta;
            ball.pos.z += ball.vec.z * delta;
            ball.vec.x += ball.acc.x * delta;
            ball.vec.y += ball.acc.y * delta;

            if ((ball.pos.x + ball_radius) > 1.0f) {
                ball.pos.x = 1.0f - ball_radius;
                ball.vec.x *= -1;
            } else if ((ball.pos.x - ball_radius) < -1.0f) {
                ball.pos.x = -1.0f + ball_radius;
                ball.vec.x *= -1;
            }
            if ((ball.pos.y + ball_radius) > aspect_ratio) {
                ball.pos.y = aspect_ratio - ball_radius;
                ball.vec.y *= -1;
            } else if ((ball.pos.y - ball_radius) < -aspect_ratio) {
                ball.pos.y = -aspect_ratio + ball_radius;
                ball.vec.y *= -1;
            }

            if ((ball.pos.z - ball_radius) < 0.0f) {
                if ((ball.pos.x + ball_radius) > (game.player[0].pos.x - paddle_size / 2.0f) &&
                    (ball.pos.x - ball_radius) < (game.player[0].pos.x + paddle_size / 2.0f) &&
                    (ball.pos.y + ball_radius) > (game.player[0].pos.y - paddle_size / 2.0f) &&
                    (ball.pos.y - ball_radius) < (game.player[0].pos.y + paddle_size / 2.0f)) {
                    ball.vec.x += ((rand() % 20) - 10) * 0.01f;
                    ball.vec.y += ((rand() % 20) - 10) * 0.01f;
                    ball.vec.z *= -speed_up;
                    ball.acc.x = game.player[0].vec.x * acc_factor;
                    ball.acc.y = game.player[0].vec.y * acc_factor;
                } else {
                    game.player[1].score += 1;
                    game.start_player = !game.start_player;
                    game.ball.pos.x = 0.0f;
                    game.ball.pos.y = 0.0f;
                    game.ball.pos.z = 5.0f;
                    ball.vec.x = ((rand() % 20) - 10) * 0.01f;
                    ball.vec.y = ((rand() % 20) - 10) * 0.01f;
                    game.ball.vec.z = game.start_player ? 2.0f : -2.0f;
                }
            } else if ((ball.pos.z + ball_radius) > 10.0f) {
                cout << "ball: (" << ball.pos.x << ", " << ball.pos.y << ")" << endl;
                cout << "paddle: (" << game.player[1].pos.x << ", " << game.player[1].pos.y << ")" << endl;
                if ((ball.pos.x + ball_radius) > (game.player[1].pos.x - paddle_size / 2.0f) &&
                    (ball.pos.x - ball_radius) < (game.player[1].pos.x + paddle_size / 2.0f) &&
                    (ball.pos.y + ball_radius) > (game.player[1].pos.y - paddle_size / 2.0f) &&
                    (ball.pos.y - ball_radius) < (game.player[1].pos.y + paddle_size / 2.0f)) {
                    ball.vec.x += ((rand() % 20) - 10) * 0.01f;
                    ball.vec.y += ((rand() % 20) - 10) * 0.01f;
                    ball.vec.z *= -speed_up;
                    ball.acc.x = game.player[1].vec.x * acc_factor;
                    ball.acc.y = game.player[1].vec.y * acc_factor;
                } else {
                    game.player[0].score += 1;
                    game.start_player = !game.start_player;
                    game.ball.pos.x = 0.0f;
                    game.ball.pos.y = 0.0f;
                    game.ball.pos.z = 5.0f;
                    ball.vec.x = ((rand() % 20) - 10) * 0.01f;
                    ball.vec.y = ((rand() % 20) - 10) * 0.01f;
                    game.ball.vec.z = game.start_player ? 2.0f : -2.0f;
                }
            }

            std::ostringstream out_data_str;
            ptree out_data_tree;
            out_data_tree.put("obj", "ball");
            out_data_tree.put("px", game.ball.pos.x);
            out_data_tree.put("py", game.ball.pos.y);
            out_data_tree.put("pz", game.ball.pos.z);
            write_json(out_data_str, out_data_tree);
            server.send_all(out_data_str.str());
        }
    }

private:
    server_t& server;
    std::thread physics_thread;
};

int main(int argc, char** argv)
{
    try {
        srand(time(0));

        game.start_player = rand() % 2;
        server_t server;
        ball_physics bp{ server };

        while (true) {
            while (packet_queue.empty()) {}

            packet_t packet;
            {
                std::lock_guard<std::mutex> lock{ packet_queue_mutex };
                packet = packet_queue.front();
                packet_queue.pop_back();
            }
            std::istringstream in_data_str{ packet.data };
            ptree in_data_tree;
            read_json(in_data_str, in_data_tree);

            string req = in_data_tree.get<string>("req");
            if (game.status == game_t::status_enum::idle) {
                string dev = in_data_tree.get<string>("dev");
                if (req == "connect") {
                    if (dev == "phone") {
                        size_t id;
                        if (!game.player[0].joined[0]) {
                            id = 0;
                        } else if (!game.player[1].joined[0]) {
                            id = 1;
                        } else {
                            throw std::runtime_error("too many phones");
                        }
                        game.player[id].joined[0] = true;
                        game.player[id].addr[0] = packet.addr;
                        //cout << "player 0 phone @ " << game.player[id].addr[0].ip << ":" << game.player[id].addr[0].port << endl;
                        cout << "phone " << id << " connected" << endl;

                        std::ostringstream out_data_str;
                        ptree out_data_tree;
                        out_data_tree.put("id", id);
                        write_json(out_data_str, out_data_tree);
                        server.send(game.player[id].addr[0], out_data_str.str());
                    } else if (dev == "laptop") {
                        size_t id;
                        if (!game.player[0].joined[1]) {
                            id = 0;
                        } else if (!game.player[1].joined[1]) {
                            id = 1;
                        } else {
                            throw std::runtime_error("too many phones");
                        }
                        game.player[id].joined[1] = true;
                        game.player[id].addr[1] = packet.addr;
                        //cout << "player 0 laptop @ " << game.player[id].addr[1].ip << ":" << game.player[id].addr[1].port << endl;
                        cout << "laptop " << id << " connected" << endl;

                        std::ostringstream out_data_str;
                        ptree out_data_tree;
                        out_data_tree.put("id", id);
                        write_json(out_data_str, out_data_tree);
                        server.send(game.player[id].addr[1], out_data_str.str());
                    }
                    if (game.player[0].joined[0] && game.player[0].joined[1] &&
                        game.player[1].joined[0] && game.player[1].joined[1]) {
                        game.status = game_t::status_enum::ready;
                    }
                }
            } else if (game.status == game_t::status_enum::ready) {
                if (req == "update") {
                    string px = in_data_tree.get<string>("px");
                    string py = in_data_tree.get<string>("py");
                    string vx = in_data_tree.get<string>("vx");
                    string vy = in_data_tree.get<string>("vy");
                    int id = std::atoi(in_data_tree.get<string>("id").c_str());

                    game.player[id].pos.x = std::atof(px.c_str());
                    game.player[id].pos.y = std::atof(py.c_str());
                    game.player[id].vec.x = std::atof(vx.c_str());
                    game.player[id].vec.y = std::atof(vy.c_str());

                    std::ostringstream out_data_str;
                    ptree out_data_tree;
                    out_data_tree.put("obj", "paddle");
                    out_data_tree.put("id", id);
                    out_data_tree.put("px", px);
                    out_data_tree.put("py", py);
                    write_json(out_data_str, out_data_tree);
                    server.send_all(out_data_str.str());
                } else if (req == "start") {
                    cout << "start" << endl;
                    bp.start();

                    game.status = game_t::status_enum::play;
                } else if (req == "switch") {
                    std::swap(game.player[0].addr[1], game.player[1].addr[1]);
                }
            } else if (game.status == game_t::status_enum::play) {
                if (req == "update") {
                    string px = in_data_tree.get<string>("px");
                    string py = in_data_tree.get<string>("py");
                    string vx = in_data_tree.get<string>("vx");
                    string vy = in_data_tree.get<string>("vy");
                    int id = std::atoi(in_data_tree.get<string>("id").c_str());

                    game.player[id].pos.x = id ? -std::atof(px.c_str()) : std::atof(px.c_str());
                    game.player[id].pos.y = std::atof(py.c_str());
                    game.player[id].vec.x = id ? -std::atof(vx.c_str()) : std::atof(vx.c_str());
                    game.player[id].vec.y = std::atof(vy.c_str());

                    std::ostringstream out_data_str;
                    ptree out_data_tree;
                    out_data_tree.put("obj", "paddle");
                    out_data_tree.put("id", id);
                    out_data_tree.put("px", px);
                    out_data_tree.put("py", py);
                    write_json(out_data_str, out_data_tree);
                    server.send_all(out_data_str.str());
                }
            }
        }

    } catch (std::runtime_error e) {
        cout << "error: " << e.what() << endl;
    }
}


#include <vector>
#include <boost/thread.hpp>
#include <iomanip>

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include "caffe/layers/bbtxt_loss_layer.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/util/rng.hpp"
#include "caffe/internal_threadpool.hpp"
#include "caffe/util/blocking_queue.hpp"
#include "caffe/util/blocking_counter.hpp"
#include "caffe/util/benchmark.hpp"

namespace caffe {

template <typename Dtype>
BBTXTLossLayer<Dtype>::BBTXTLossLayer (const LayerParameter &param)
    : LossLayer<Dtype>(param),
      InternalThreadpool(4),
      _b_queue()
{
    CHECK(param.has_accumulator_loss_param()) << "AccumulatorLossParameter is mandatory!";
    CHECK(param.accumulator_loss_param().has_radius()) << "Radius is mandatory!";
    CHECK(param.accumulator_loss_param().has_circle_ratio()) << "Circle ratio is mandatory!";
    CHECK(param.accumulator_loss_param().has_downsampling()) << "Downsampling is mandatory!";
}

template <typename Dtype>
BBTXTLossLayer<Dtype>::~BBTXTLossLayer ()
{
    StopInternalThreadpool();
}

template <typename Dtype>
void BBTXTLossLayer<Dtype>::LayerSetUp (const vector<Blob<Dtype>*> &bottom, const vector<Blob<Dtype>*> &top)
{
    // IMPORTANT: The label blob is first bottom[0] and then the accumulator bottom[1]

    LossLayer<Dtype>::LayerSetUp(bottom, top);

    this->_accumulator = std::make_shared<Blob<Dtype>>();
    this->_diff        = std::make_shared<Blob<Dtype>>();

    this->_accumulator->ReshapeLike(*bottom[1]);
    this->_diff->ReshapeLike(*bottom[1]);

    // Scale of this accumulator is given by downsampling
    this->_scale = this->layer_param_.accumulator_loss_param().downsampling();
    // Compute the bounds for bounding boxes, which should be included in this accumulator
    this->_computeSizeBounds();

    this->StartInternalThreadpool();
}

template <typename Dtype>
void BBTXTLossLayer<Dtype>::Reshape (const vector<Blob<Dtype>*> &bottom, const vector<Blob<Dtype>*> &top)
{
    // IMPORTANT: The label blob is first bottom[0] and then the accumulator bottom[1]

    LossLayer<Dtype>::Reshape(bottom, top);

    CHECK_EQ(bottom[1]->shape(1), 5) << "Accumulator must have 5 channels (prob, xmin, ymin, xmax, ymax)!";

    this->_accumulator->ReshapeLike(*bottom[1]);
    this->_diff->ReshapeLike(*bottom[1]);
}

template <typename Dtype>
void BBTXTLossLayer<Dtype>::Forward_cpu (const vector<Blob<Dtype>*> &bottom, const vector<Blob<Dtype>*> &top)
{
    const int batch_size = bottom[1]->shape(0);

    this->_loss_prob      = Dtype(0.0f);
    this->_loss_prob_pos  = Dtype(0.0f);
    this->_loss_prob_neg  = Dtype(0.0f);
    this->_loss_coord     = Dtype(0.0f);

    this->_num_processed.reset();

    // We have to store the labels and net output in internal members because we will be accessing them in
    // threads
    this->_labels = bottom[0]; this->_labels->cpu_data();
    this->_bottom = bottom[1]; this->_bottom->cpu_data();

    // -- COMPUTE THE LOSS -- //
    // Go through all images on the output and for each of them create accumulators and compute loss
    for (int b = 0; b < this->_labels->shape(0); ++b) this->_b_queue.push(b);
    // Wait for the threadpool to finish processing the images
    this->_num_processed.waitToCount(batch_size);

    // Loss per pixel
    Dtype loss = (this->_loss_prob/batch_size + this->_loss_coord/batch_size) / Dtype(2.0f);

    const int ds = this->layer_param_.accumulator_loss_param().downsampling();
    std::cout << std::fixed << std::showpoint << std::setprecision(6)
              << "loss_prob_pos_x" << ds << ": \t" << (this->_loss_prob_pos / batch_size) << ", \t"
              << "loss_prob_neg_x" << ds << ": \t" << (this->_loss_prob_neg / batch_size) << ", \t"
              << "loss_prob_x" << ds << ": \t" << (this->_loss_prob / batch_size) << ", \t"
              << "loss_coord_x" << ds << ": \t" << (this->_loss_coord / batch_size) << std::endl;

    top[0]->mutable_cpu_data()[0] = loss;
}

template <typename Dtype>
void BBTXTLossLayer<Dtype>::Backward_cpu (const vector<Blob<Dtype>*> &top,
                                                          const vector<bool> &propagate_down,
                                                          const vector<Blob<Dtype>*> &bottom)
{
    Dtype downsampling = this->layer_param_.accumulator_loss_param().downsampling();

    // Fill in the diff (gradient) for each output blob
    if (propagate_down[1])
    {
        // Scale the gradient - they scale it everywhere with batch size (bottom[i]->shape(0)), I don't know
        // why but lets do it as well
        //
        // IMPORTANT! Since in fully convolutional network the gradients are accumulated, NOT averaged!, we
        // have scale the gradient according to the number of pixels in the output accumulator. The reference
        // number of pixels is given by the x1 accumulator
        const Dtype alpha = top[0]->cpu_diff()[0] / bottom[1]->shape(0) * downsampling*downsampling;

        caffe_cpu_axpby(bottom[1]->count(), alpha, this->_diff->cpu_data(), Dtype(0.0f),
                        bottom[1]->mutable_cpu_diff());
    }
}

template <typename Dtype>
void BBTXTLossLayer<Dtype>::InternalThreadpoolEntry (int t)
{
    // This method runs on each thread of the internal threadpool
    // We build accumulator, remove negative cooordinate diff and apply diff weights for each image in
    // the batch

    try {
        while (!this->must_stopt(t))
        {
            int b = this->_b_queue.pop();

            // Process this image
            this->_buildAccumulator(b);

            const int batch_size    = this->_bottom->shape(0);
            const int count         = this->_bottom->count() / batch_size;
            const int count_channel = this->_bottom->shape(2) * this->_bottom->shape(3);

            // Compute the difference of the target accumulator and the estimated one
            caffe_sub(count, this->_bottom->cpu_data() + this->_bottom->offset(b, 0),
                             this->_accumulator->cpu_data() + this->_accumulator->offset(b, 0),
                             this->_diff->mutable_cpu_data() + this->_diff->offset(b, 0));

            // We do not want to include errors on coordinates from samples (pixels), which are not supposed
            // to predict them - we need to remove the computed difference from the loss computation
            int num_removed_coords = this->_removeNegativeCoordinateDiff(b);

            // Loss from the probability accumulator (channel 0)
            this->_computeProbabilityLoss(b);
            // Loss from the coordinates (channels 1-4)
            const Dtype *data_coord = this->_diff->cpu_data() + this->_diff->offset(b, 1);
            Dtype loss_coord = caffe_cpu_dot(4*count_channel, data_coord, data_coord);

            // Loss per pixel
            if (num_removed_coords != 4*count_channel)
            {
                this->_loss_coord = this->_loss_coord + loss_coord / (4*count_channel - num_removed_coords);
            }

            // In the training pass we also have to adjust the diffs (for testing we do not need this)
            if (this->phase_ == TRAIN)
            {
                // Apply weight on the diffs to compensate for the smaller amount of positive pixels in
                // the target accumulator
                this->_applyDiffWeights(b);
            }

            // Raise the counter on processed images
            this->_num_processed.increase();

        }
    } catch (boost::thread_interrupted&) {
        // Interrupted exception is expected on shutdown
    }
}

// -----------------------------------------  PROTECTED METHODS  ----------------------------------------- //

template <typename Dtype>
void BBTXTLossLayer<Dtype>::_computeSizeBounds ()
{
    // The bounding boxes that should be detected by a given accumulator are determined by their size. Each
    // accumulator detects bounding boxes with size in some interval <min bound, max bound>. Here we compute
    // these bounds. These bounds are also not sharp, but they overlap so some objects will be detected by
    // multiple accumulators

    const int radius = this->layer_param_.accumulator_loss_param().radius();
    const float cr   = this->layer_param_.accumulator_loss_param().circle_ratio();
    const float bo   = this->layer_param_.accumulator_loss_param().bounds_overlap();

    this->_bb_bounds = std::make_pair(0.0f, 99999.9f);

    // The "ideal" size of a bounding box that should be detected by this accumulator
    this->_ideal_size = float(2*radius+1) / cr * this->_scale;

    // Bound above
    const float ext_above  = ((1-bo)*this->_ideal_size)/2 + bo*this->_ideal_size;
    this->_bb_bounds.second = this->_ideal_size + ext_above;

    // Bound below
    const float difference = this->_ideal_size / 2.0f;
    const float ext_below  = ((1-bo)*difference)/2 + bo*difference;
    this->_bb_bounds.first = this->_ideal_size - ext_below;

    LOG(INFO) << "Scale: x" << this->_scale << ", bbs: " << this->_bb_bounds.first << " to "
              << this->_bb_bounds.second << " px, ideal: " << this->_ideal_size << " px";
}

template <typename Dtype>
void BBTXTLossLayer<Dtype>::_buildAccumulator (int b)
{
    // This is because of the cv::Mat type - I don't want to program other types now except CV_32FC1
    CHECK((std::is_same<Dtype,float>::value)) << "BBTXTLossLayer supports only float!";

    // Radius of the circle drawn in the center of the bounding box
    const int radius = this->layer_param_.accumulator_loss_param().radius();

    const int height = this->_accumulator->shape(2);
    const int width  = this->_accumulator->shape(3);

    Dtype *accumulator_data = this->_accumulator->mutable_cpu_data();

    const double scaling_ratio = 1.0 / this->_scale;

    // The channels of the accumulator go like this: probability, xmin, ymin, xmax, ymax
    for (int c = 0; c < 5; ++c)
    {
        // Create a cv::Mat wrapper for the accumulator - this way we can now use OpenCV drawing functions
        // to create circles in the accumulator
        cv::Mat acc(height, width, CV_32FC1, accumulator_data + this->_accumulator->offset(b, c));
        acc.setTo(cv::Scalar(0));

        // Draw circles in the center of the bounding boxes
        for (int i = 0; i < this->_labels->shape(1); ++i)
        {
            // Data are stored like this [label, xmin, ymin, xmax, ymax]
            const Dtype *data = this->_labels->cpu_data() + this->_labels->offset(b, i);

            // If the label is -1, there are no more bounding boxes
            if (data[0] == Dtype(-1.0f)) break;

            // Check if the size of the bounding box fits within the bounds of this accumulator - the
            // largest dimension of the bounding box has to by within the bounds
            Dtype largest_dim = std::max(data[3]-data[1], data[4]-data[2]);
            if (largest_dim >= this->_bb_bounds.first && largest_dim <= this->_bb_bounds.second)
            {
                const int x = (data[1]+data[3]) / 2;
                const int y = (data[2]+data[4]) / 2;

                // Either 1 for probability or coordinates relative to the object centroid
                if (c == 0)
                {
                    cv::circle(acc, cv::Point(scaling_ratio*x,
                                              scaling_ratio*y), radius, cv::Scalar(Dtype(1.0f)), -1);
                }
                else
                {
                    this->_renderCoordinateCircle(acc, x, y, data[c], c);
                }
            }
        }

        // We want to create Gaussians in the probability accumulator, not just sharp circles
        if (c == 0)
        {
            cv::GaussianBlur(acc, acc, cv::Size(3, 3), 1);
            double mx; cv::minMaxLoc(acc, 0, &mx);
            if (mx != 0.0) acc *= 1.0 / mx;
        }
    }
}

template <typename Dtype>
int BBTXTLossLayer<Dtype>::_removeNegativeCoordinateDiff (int b)
{
    int num_removed = 0;

    // The channels of the accumulator go like this: probability, xmin, ymin, xmax, ymax - we want to
    // nullify the diffs of the coordinates, thus indices 1 to 4
    for (int c = 1; c < 5; ++c)
    {
        const Dtype *data_acc_prob = this->_accumulator->cpu_data() + this->_accumulator->offset(b, 0);
        Dtype *data_diff_coord     = this->_diff->mutable_cpu_data() + this->_diff->offset(b, c);

        for (int i = 0; i < this->_accumulator->shape(2)*this->_accumulator->shape(3); ++i)
        {
            // This is a negative sample - nullify coordinate diff
            if (*data_acc_prob == Dtype(0.0f))
            {
                *data_diff_coord = Dtype(0.0f);
                num_removed++;
            }
            else
            {
                *data_diff_coord *= *data_acc_prob;
            }

            data_acc_prob++;
            data_diff_coord++;
        }
    }

    return num_removed;
}

template <typename Dtype>
void BBTXTLossLayer<Dtype>::_computeProbabilityLoss (int b)
{
    const int count_channel = this->_bottom->shape(2) * this->_bottom->shape(3);

    int num_pos    = 0;
    Dtype loss_neg = Dtype(0.0f);
    Dtype loss_pos = Dtype(0.0f);

    const Dtype *data_acc_prob  = this->_accumulator->cpu_data() + this->_accumulator->offset(b, 0);
    const Dtype *data_diff_prob = this->_diff->cpu_data() + this->_diff->offset(b, 0);
    for (int i = 0; i < count_channel; ++i)
    {
        if (*data_acc_prob == Dtype(0.0f))
        {
            // Negative sample
            loss_neg += (*data_diff_prob) * (*data_diff_prob);
        }
        else
        {
            // Positive sample
            loss_pos += (*data_diff_prob) * (*data_diff_prob);
            num_pos++;
        }

        data_acc_prob++;
        data_diff_prob++;
    }

    this->_loss_prob = this->_loss_prob + (loss_pos+loss_neg) / count_channel;
    this->_loss_prob_pos = this->_loss_prob_pos + loss_pos / (num_pos > 0 ? num_pos : 1);
    this->_loss_prob_neg = this->_loss_prob_neg + loss_neg / (count_channel-num_pos);
}

template <typename Dtype>
void BBTXTLossLayer<Dtype>::_applyDiffWeights (int b)
{
    // Applies weights on the diffs in order to even the impact of the positive and negative samples on
    // the gradient

    const int count_channel = this->_accumulator->shape(2) * this->_accumulator->shape(3);
    const float nr          = this->layer_param().accumulator_loss_param().negative_ratio();
//    const Dtype HN_THRESH   = 0.25f;

    const Dtype* data_acc_prob  = this->_accumulator->cpu_data() + this->_accumulator->offset(b, 0);
//    const Dtype *data_diff_prob = this->_diff->cpu_data() + this->_diff->offset(b, 0);
    std::vector<Dtype*> data_diff_m;
    for (int c = 0; c < this->_diff->shape(1); ++c)
    {
        data_diff_m.push_back(this->_diff->mutable_cpu_data() + this->_diff->offset(b, c));
    }

    // Number of positive pixels (samples) in this accumulator
    const float pn = caffe_cpu_asum(count_channel, data_acc_prob);
    const float nn = count_channel - pn;

    // Compute the number of hard negative pixels (samples)
//    float hnn = 0.0f;
//    for (int i = 0; i < count_channel; ++i)
//    {
//        // We do not have to check if this is a positive or negative sample because we will never have
//        // a huge positive error on positive samples - the network learns to predict values in [0, 1],
//        // therefore it can only overshoot a value for negative samples, which should be zero. The diff
//        // is therefore positive
//        if (*data_diff_prob > HN_THRESH) hnn++;  // This is a hard negative
//        data_diff_prob++;
//    }

    // Now determine the weight of positive pixels from the required ratio
    const float pos_diff_weight = nn / pn / nr;
    // The weight of negative pixels is adjusted so that hard negatives have the same weigth sum as
    // the other samples' diffs
//    const float neg_diff_weight  = (hnn == 0) ? 1.0f : (nn / (nn-hnn) / 2.0f);
//    const float hneg_diff_weight = (hnn == 0) ? 1.0f : (nn / hnn / 2.0f);

    for (int i = 0; i < count_channel; ++i)
    {
        if (pn == 0)
        {
            // If there is no positive blob, then we want to ignore gradients from this accumulator durning
            // backpropagation
            for (int c = 0; c < data_diff_m.size(); ++c)
            {
                data_diff_m[c][i] = Dtype(0.0f);
            }
        }
        else
        {
            if (*data_acc_prob > Dtype(0.0f))
            {
                // Positive sample
                for (int c = 0; c < data_diff_m.size(); ++c)
                {
                    data_diff_m[c][i] *= pos_diff_weight;
                }
            }
//            else if (*data_diff_prob_m > HN_THRESH)
//            {
//                // Hard negative sample
//                *data_diff_prob_m *= hneg_diff_weight;
//            }
//            else
//            {
//                // Other negative samples
//                *data_diff_prob_m *= neg_diff_weight;
//            }
        }

        data_acc_prob++;
    }
}

template <typename Dtype>
void BBTXTLossLayer<Dtype>::_renderCoordinateCircle (cv::Mat &acc, int x, int y, Dtype value, int channel)
{
    // The circles in the accumulators with the coordinates have to be rendered separately because the value
    // of each pixel differs based on the pixel position inside of the bounding box

    const Dtype DUMMY = 9999.0f;
    const int radius  = this->layer_param_.accumulator_loss_param().radius() + 1;
    const int x_acc   = x / this->_scale;
    const int y_acc   = y / this->_scale;

    // Because I don't want to take care of the plotting of the circle myself, I use the OpenCV function and
    // then replace the values - first create a circle with a dummy value
    cv::circle(acc, cv::Point(x_acc, y_acc), radius-1, cv::Scalar(DUMMY), -1);
    cv::GaussianBlur(acc, acc, cv::Size(3, 3), 100);

    // Now go through the pixels in the circle's bounding box and if there is the DUMMY value compute
    // the real value
    for (int i = -radius; i <= radius; ++i)
    {
        for (int j = -radius; j <= radius; ++j)
        {
            int xp = x_acc + j;
            int yp = y_acc + i;

            if (xp >= 0 && xp < acc.cols && yp >= 0 && yp < acc.rows)
            {
                // Pixel is inside of the accumulator - check if it contains DUMMY
                if (acc.at<Dtype>(yp, xp) > Dtype(DUMMY/100.0f))
                {
                    // Change its value to the actual value - coordinate relative to the current pixel position
                    // The coordinates are converted to approximately [0,1], i.e. the ideal bounding box has
                    // coordinates [0,0], [1,1], but the actual bounding boxes will differ in both dimensions
                    if (channel == 1 || channel == 3)
                    {
                        // xmin, xmax
                        acc.at<Dtype>(yp, xp) = Dtype(0.5f + (value-x - j*this->_scale) / this->_ideal_size);
                    }
                    else if (channel == 2 || channel == 4)
                    {
                        // ymin, ymax
                        acc.at<Dtype>(yp, xp) = Dtype(0.5f + (value-y - i*this->_scale) / this->_ideal_size);
                    }
                }
            }
        }
    }
}

// ----------------------------------------  LAYER INSTANTIATION  ---------------------------------------- //

#ifdef CPU_ONLY
//STUB_GPU(BBTXTLossLayer);
#endif

INSTANTIATE_CLASS(BBTXTLossLayer);
REGISTER_LAYER_CLASS(BBTXTLoss);

}  // namespace caffe


#include "bwd_lagrangian.h"

#include <cmath>
#include <Eigen/Dense>

#include "../amr/mesh.h"
#include "../amr/numerics.h"
#include "../amr/refiner.h"

using namespace Eigen;

namespace vlv { namespace tools {
template<typename T>
T modf(T arg, T* iptr) = delete;

template<>
float modf(float arg, float* iptr) { return modff(arg, iptr); }

template<>
double modf(double arg, double* iptr) { return modf(arg, iptr); }

} }

template<typename T, int D, int V>
void vlv::AmrMomentumLagrangianSolver<T,D,V>::solve_mesh(
        toolbox::AdaptiveMesh<T, 3>& mesh0,
        toolbox::AdaptiveMesh<T, 3>& mesh1,
        vec& Einc,
        vec& Binc,
        tools::Params<T>& params)
{

  toolbox::Adapter<T,3> adapter;
  adapter.cells_to_refine.clear();
  adapter.cells_to_unrefine.clear();

  // empty the target mesh
  // TODO: is this efficient; should we recycle instead?
  mesh1.clear();

  std::array<uint64_t, 3> index;
  // std::array<T, 3> grad;

  Vec3E B(Binc.data());
  Vec3E E(Einc.data());

  // level zero fill
  auto val     = T(0);
  T max_val = mesh0.max_value();

  T refine_indicator, unrefine_indicator;
  auto len = mesh0.get_size(0);

  // XXX remove higher-dimension updates (compresses inner loops away)
  // TODO does not work if the fill is not correctly done
  for(int i = 2; i>V-1; i--) len[i] = 1;

  // normalize
  //T norm0 = integrate_moment( mesh0,
  //          [](std::array<T,3>& uvel) -> T { return T(1);}
  //          );

  // cfl bound guards
  auto cids = mesh0.get_cells(true);

  // if mesh is empty, bail out
  if (cids.empty()) { return; }

  auto min_ind = mesh0.get_indices( cids.front() );
  auto max_ind = mesh0.get_indices( cids.back()  );

  int cfl_halo = 10; // how many CFL steps are allowed in backward substitution

  for(uint64_t t=0; t<len[2]; t++) {
    index[2] = t;
    for(uint64_t s=0; s<len[1]; s++) {
      index[1] = s;
      for(uint64_t r=0; r<len[0]; r++) {
        index[0] = r;

        // cfl bound guards
        if( r < (min_ind[0] - cfl_halo) ) continue;
        if( r > (max_ind[0] + cfl_halo) ) continue;

        uint64_t cid = mesh1.get_cell_from_indices(index, 0);
        val = backward_advect(index, 0, mesh0, E, B, params);

        // refinement
        // TODO specialize to value & gradient instead of just value
        refine_indicator   = val/max_val;
        unrefine_indicator = val/max_val;
        adapter.check_cell(mesh1, cid, refine_indicator, unrefine_indicator);

        mesh1.set(cid, val);
      }
    }
  }

  // create new leafs
  // TODO fixme to use max ref. lvl of mesh
  // for(size_t sweep=1; sweep<=mesh1.maximum_refinement_level; sweep++){
  for(int sweep=1; sweep<=mesh1.top_refinement_level; sweep++){

    adapter.refine(mesh1);

    // fmt::print(" sol: cells created {}\n", adapter.cells_to_refine.size());
    // fmt::print(" sol: cells removed {}\n", adapter.cells_removed.size());

    adapter.cells_to_refine.clear();
    adapter.cells_to_unrefine.clear();

    // next we refine
    for(auto&& cid : adapter.cells_created) {
      int rfl = mesh1.get_refinement_level(cid);
      auto index2 = mesh1.get_indices(cid);

      // fmt::print("creating {} at {}\n", cid, rfl);

      val = backward_advect(index2, rfl, mesh0, E, B, params);
      mesh1.set(cid, val);

      refine_indicator   = val/max_val;
      unrefine_indicator = val/max_val;
      adapter.check_cell(mesh1, cid, refine_indicator, unrefine_indicator);
    }

    // now unrefine
    adapter.unrefine(mesh1);

  }

  // normalize back to original weight
  //T norm1 = integrate_moment( mesh1,
  //          [](std::array<T,3>& uvel) -> T { return T(1);}
  //          );
  //mesh1 *= norm0/norm1;

  }

// backward advected Lorentz force
template<typename T, int D, int V>
T vlv::AmrMomentumLagrangianSolver<T,D,V>::backward_advect(
    std::array<uint64_t, 3>& index,
    int rfl,
    const toolbox::AdaptiveMesh<T, 3>& mesh0,
    //toolbox::AdaptiveMesh<T, 3>& mesh1,
    Vec3E& E,
    Vec3E& B,
    tools::Params<T>& params)
{
  T val; // return value
  vec u    = mesh0.get_center(index, rfl);  // velocity
  vec du   = mesh0.get_length(rfl);         // box size, i.e., \Delta u
  auto len = mesh0.get_size(rfl);

  // get shift of the characteristic solution from Lorentz force
  Vec3E uvel( u.data() );
  Vec3E F = lorentz_force(uvel, E, B, params.qm, params.cfl);

  // add other forces; default to zero
  Vec3E Fi = other_forces(uvel, params);
  F += Fi;

  // advection in units of cells
  // NOTE: We normalize with CFL because velocities are in units
  // of c and we need them in units of grid speed.
  // XXX: CHECK
  std::array<T,3> shift, cell_shift;
  for(int i=0; i<V; i++) shift[i] = F(i) / du[i];

  // advected tiles
  std::array<int,3> index_shift;
  for(int i=0; i<V; i++) index_shift[i] = static_cast<int>( trunc(shift[i]) );

  // advection inside cell
  T tmp;
  for(int i=0; i<V; i++) cell_shift[i] = tools::modf<T>(shift[i], &tmp);

  // new grid indices
  std::array<uint64_t, 3> index_new;
  for(int i=0;   i<V;   i++) index_new[i] = index[i] + index_shift[i];
  for(int i = 2; i>V-1; i--) index_new[i] = index[i];

  // set boundary conditions (zero outside the grid limits)
  for(int i=0; i<V; i++){
    if( (index_new[i] <2) || (index_new[i] >= len[i]-2) ) return T(0);
  }

  // interpolation branch
  val = toolbox::interp_cubic<T,V>(mesh0, index_new, cell_shift, rfl);
  //val = toolbox::interp_linear<T,V>(mesh0, index_new, cell_shift, rfl);

  return val;
}

/// Relativistic Lorentz force / Electrostatic version
template<typename T, int D, int V>
inline typename vlv::AmrMomentumLagrangianSolver<T,D,V>::Vec3E
vlv::AmrMomentumLagrangianSolver<T,D,V>::lorentz_force(
  Vec3E& /*uvel*/,
  Vec3E& E,
  Vec3E& /*B*/,
  T qm,
  T cfl)
{
  // electrostatic push
  //
  // Boris scheme for b=0 translates to
  // u = (cfl*u_0 + e + e)/cfl = u_0 + E/cfl
  //
  // with halving taken into account in definition of Ex
  return -qm*E/cfl;
}

/// default zero force for to be overloaded by more complicated solvers
template<typename T, int D, int V>
inline typename vlv::AmrMomentumLagrangianSolver<T,D,V>::Vec3E
vlv::AmrMomentumLagrangianSolver<T,D,V>::other_forces(
    Vec3E& /*uvel*/,
    tools::Params<T>& /*params*/
    )
{
  Vec3E ret = Vec3E::Zero();
  return ret;
}

//--------------------------------------------------
// explicit template instantiation
template class vlv::AmrMomentumLagrangianSolver<Realf, 1, 1>; //1D1V


// Copyright (c) 2014-2021 The Gridcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "gridcoin/magnitude.h"
#include "streams.h"

#include <iostream>
#include <boost/test/unit_test.hpp>

// -----------------------------------------------------------------------------
// Magnitude
// -----------------------------------------------------------------------------

BOOST_AUTO_TEST_SUITE(Magnitude)

BOOST_AUTO_TEST_CASE(it_initializes_to_zero_magnitude)
{
    const GRC::Magnitude mag = GRC::Magnitude::Zero();

    BOOST_CHECK_EQUAL(mag.Scaled(), 0);
}

BOOST_AUTO_TEST_CASE(it_initializes_to_zero_when_rounding_invalid_magnitudes)
{
    // Negative
    const GRC::Magnitude negative = GRC::Magnitude::RoundFrom(-1.234);
    BOOST_CHECK_EQUAL(negative.Scaled(), 0);

    // Rounds-down to zero
    const GRC::Magnitude effectively_zero = GRC::Magnitude::RoundFrom(0.005);
    BOOST_CHECK_EQUAL(effectively_zero.Scaled(), 0);

    // Exceeded maximum
    const GRC::Magnitude overflow = GRC::Magnitude::RoundFrom(32767.123);
    BOOST_CHECK_EQUAL(overflow.Scaled(), 0);
}

BOOST_AUTO_TEST_CASE(it_rounds_a_small_magnitude_to_two_places)
{
    const GRC::Magnitude min = GRC::Magnitude::RoundFrom(0.0051);
    BOOST_CHECK_EQUAL(min.Scaled(), 1);

    const GRC::Magnitude max = GRC::Magnitude::RoundFrom(0.994);
    BOOST_CHECK_EQUAL(max.Scaled(), 99);
}

BOOST_AUTO_TEST_CASE(it_rounds_a_medium_magnitude_to_one_place)
{
    const GRC::Magnitude min = GRC::Magnitude::RoundFrom(0.995);
    BOOST_CHECK_EQUAL(min.Scaled(), 100);

    const GRC::Magnitude max = GRC::Magnitude::RoundFrom(9.94);
    BOOST_CHECK_EQUAL(max.Scaled(), 990);
}

BOOST_AUTO_TEST_CASE(it_rounds_a_large_magnitude_to_a_whole)
{
    const GRC::Magnitude min = GRC::Magnitude::RoundFrom(9.95);
    BOOST_CHECK_EQUAL(min.Scaled(), 1000);

    const GRC::Magnitude max = GRC::Magnitude::RoundFrom(32767.0);
    BOOST_CHECK_EQUAL(max.Scaled(), 3276700);
}

BOOST_AUTO_TEST_CASE(it_compares_another_magnitude_for_equality)
{
    const GRC::Magnitude magnitude1 = GRC::Magnitude::RoundFrom(1.23);
    const GRC::Magnitude magnitude2 = GRC::Magnitude::RoundFrom(1.23);

    BOOST_CHECK(magnitude1 == magnitude2);
    BOOST_CHECK(magnitude1 != GRC::Magnitude::Zero());
}

BOOST_AUTO_TEST_CASE(it_compares_an_integer_for_equality)
{
    const GRC::Magnitude magnitude = GRC::Magnitude::RoundFrom(123);

    BOOST_CHECK(magnitude == 123);
    BOOST_CHECK(magnitude != 999);
}

BOOST_AUTO_TEST_CASE(it_compares_a_floating_point_number_for_equality)
{
    const GRC::Magnitude magnitude = GRC::Magnitude::RoundFrom(1.23);

    // Floating-point comparisons round the other value according to the size
    // for the precision tiers before checking equality:
    BOOST_CHECK(magnitude == 1.23);
    BOOST_CHECK(magnitude == 1.2);
    BOOST_CHECK(magnitude == 1.25);
    BOOST_CHECK(magnitude != 1.26);
}

BOOST_AUTO_TEST_CASE(it_reports_its_size_category)
{
    using Kind = GRC::Magnitude::Kind;

    const GRC::Magnitude zero = GRC::Magnitude::Zero();
    BOOST_CHECK(zero.Which() == Kind::ZERO);

    const GRC::Magnitude effectively_zero = GRC::Magnitude::RoundFrom(0.005);
    BOOST_CHECK(effectively_zero.Which() == Kind::ZERO);

    const GRC::Magnitude min_small = GRC::Magnitude::RoundFrom(0.0051);
    BOOST_CHECK(min_small.Which() == Kind::SMALL);

    const GRC::Magnitude max_small = GRC::Magnitude::RoundFrom(0.994);
    BOOST_CHECK(max_small.Which() == Kind::SMALL);

    const GRC::Magnitude min_medium = GRC::Magnitude::RoundFrom(0.995);
    BOOST_CHECK(min_medium.Which() == Kind::MEDIUM);

    const GRC::Magnitude max_medium = GRC::Magnitude::RoundFrom(9.94);
    BOOST_CHECK(max_medium.Which() == Kind::MEDIUM);

    const GRC::Magnitude min_large = GRC::Magnitude::RoundFrom(9.95);
    BOOST_CHECK(min_large.Which() == Kind::LARGE);

    const GRC::Magnitude max_large = GRC::Magnitude::RoundFrom(32767.0);
    BOOST_CHECK(max_large.Which() == Kind::LARGE);
}

BOOST_AUTO_TEST_CASE(it_presents_the_scaled_magnitude_representation)
{
    const GRC::Magnitude small = GRC::Magnitude::RoundFrom(0.11);
    BOOST_CHECK_EQUAL(small.Scaled(), 11);

    const GRC::Magnitude medium = GRC::Magnitude::RoundFrom(1.1);
    BOOST_CHECK_EQUAL(medium.Scaled(), 110);

    const GRC::Magnitude large = GRC::Magnitude::RoundFrom(11.0);
    BOOST_CHECK_EQUAL(large.Scaled(), 1100);
}

BOOST_AUTO_TEST_CASE(it_presents_the_compact_magnitude_representation)
{
    const GRC::Magnitude small = GRC::Magnitude::RoundFrom(0.11);
    BOOST_CHECK_EQUAL(small.Compact(), 11);

    const GRC::Magnitude medium = GRC::Magnitude::RoundFrom(1.1);
    BOOST_CHECK_EQUAL(medium.Compact(), 11);

    const GRC::Magnitude large = GRC::Magnitude::RoundFrom(11.0);
    BOOST_CHECK_EQUAL(large.Compact(), 11);
}

BOOST_AUTO_TEST_CASE(it_presents_the_floating_point_magnitude_representation)
{
    const GRC::Magnitude small = GRC::Magnitude::RoundFrom(0.11);
    BOOST_CHECK_EQUAL(small.Floating(), 0.11);

    const GRC::Magnitude medium = GRC::Magnitude::RoundFrom(1.1);
    BOOST_CHECK_EQUAL(medium.Floating(), 1.1);

    const GRC::Magnitude large = GRC::Magnitude::RoundFrom(11.0);
    BOOST_CHECK_EQUAL(large.Floating(), 11.0);
}

BOOST_AUTO_TEST_CASE(it_represents_itself_as_a_string)
{
    const GRC::Magnitude zero = GRC::Magnitude::Zero();
    BOOST_CHECK_EQUAL(zero.ToString(), "0");

    const GRC::Magnitude small = GRC::Magnitude::RoundFrom(0.11);
    BOOST_CHECK_EQUAL(small.ToString(), "0.11");

    const GRC::Magnitude medium = GRC::Magnitude::RoundFrom(1.1);
    BOOST_CHECK_EQUAL(medium.ToString(), "1.1");

    const GRC::Magnitude large = GRC::Magnitude::RoundFrom(11.0);
    BOOST_CHECK_EQUAL(large.ToString(), "11");

    const GRC::Magnitude max = GRC::Magnitude::RoundFrom(32767.0);
    BOOST_CHECK_EQUAL(max.ToString(), "32767");
}

BOOST_AUTO_TEST_SUITE_END()