src/global.h

Fri, 20 Jan 2023 16:44:08 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Fri, 20 Jan 2023 16:44:08 +0100
changeset 467
c5f6f3f1b714
parent 464
1fed3ff9a64e
child 468
b21da6f583be
permissions
-rw-r--r--

Added more buttons to the images tab. Load images from the database and display thumbnails added. Added support for jpeg files. Rename pictures in the database to .png. Added temporary images_list, images_count and images_current variables to the product record.

#ifndef	_GLOBAL_H
#define	_GLOBAL_H

#include <QList>
#include <QString>
#include <QTranslator>
#include <QDate>
#include <QJsonDocument>
#include <QWebSocket>

/*
 * Debug log switches for building
 */
// #define DEBUG_IBU		1
// #define	DEBUG_FERMENTABLES	1
#define	DEBUG_YEAST		1
#define	DEBUG_WATER		1

#define Ka1			0.0000004445
#define Ka2			0.0000000000468

#define MMCa			40.078
#define MMMg			24.305
#define MMNa			22.98976928
#define MMCl			35.4535
#define MMSO4			96.0626
#define MMCO3			60.0089
#define MMNO3			62.0049
#define MMHCO3			61.01684
#define MMCaSO4			172.171
#define MMCaCl2			147.015
#define MMCaCO3			100.087
#define MMMgCl2			95.211		/* Since 27-06-2021 */
#define MMMgSO4			246.475
#define MMNaHCO3		84.007
#define MMNa2CO3		105.996
#define MMNaCl			58.443
#define MMCaOH2			74.06268

#define SpecificHeatWater	1.0
#define SpecificHeatMalt	0.399		///< cal/g.°C
#define SlakingHeat		10.318		///< cal/g.°C
#define equip_tun_weight	2.0		///< 2 Kg pot
#define equip_tun_specific_heat	0.110
#define MaltVolume		0.87		///< l/kg 0.688 volgens internetbronnen, gemeten 0.874 l/kg, na enige tijd maischen 0,715 l/kg (A3 Otten).

#define	Seapressure		1013.25		///< Air pressure at sealevel in hPa
#define	MolMassAir		0.0289644	///< Air molair mass
#define	Gravacc			9.80665		///< Gravitational acceleration in m/s2
#define	Gasconst		8.3144621	///< Gas constant J K-1 mol-1
#define Kelvin			273.15		///< Kelvin to Celsius
#define	EoVwater		40660		///< Enthalpy of Vaporization (ΔH) for water

extern QWebSocket *webSocket;

struct Acid
{
    QString     name_en;
    QString	name_nl;
    double      pK1;
    double      pK2;
    double      pK3;
    double      MolWt;
    double      AcidSG;
    double      AcidPrc;
};

extern QList<Acid>	my_acids;

/*
 * Fermentables, Hops, Miscs, Yeasts and Mashs are stored in the
 * database in json arrays. These are the QList structures.
 * For some purposes there are more fields then needed in these
 * structures such as for inventory database i/o.
 * Just use what is needed.
 */
struct Fermentables
{
    QString	name;
    QString	origin;
    QString	supplier;
    QString	notes;
    double	amount;
    double	cost;
    int		type;
    double	yield;
    double	color;
    double	coarse_fine_diff;
    double	moisture;
    double	diastatic_power;
    double	protein;
    double	dissolved_protein;
    double	max_in_batch;
    int		graintype;
    int		added;
    bool	recommend_mash;
    bool	add_after_boil;
    bool	adjust_to_total_100;
    double	percentage;
    double	di_ph;
    double	acid_to_ph_57;
    double	inventory;		///< In product, current inventory.
    bool	avail;			///< Product available in database.
};


struct Hops
{
    QString	name;
    QString	origin;
    QString	notes;
    QString	substitutes;
    double	amount;
    double	cost;
    int		type;
    int		form;
    int		useat;
    double	time;
    double	alpha;
    double	beta;
    double	hsi;
    double	humulene;
    double	caryophyllene;
    double	cohumulone;
    double	myrcene;
    double	total_oil;
    double	utilisation;
    double	bu_factor;
    double	inventory;		///< In product, current inventory.
    bool	avail;			///< Product available in database.
};


struct Miscs
{
    QString	name;
    QString	notes;
    QString	use_for;
    double	amount;
    int		type;
    int		use_use;
    double	time;
    bool	amount_is_weight;
    bool	always_on_stock;
    double	cost;
    double	inventory;		///< In product, current inventory.
    bool	avail;			///< Product available in database.
};


struct Yeasts
{
    QString	name;
    QString	laboratory;
    QString	product_id;
    QString	notes;
    QString	best_for;
    QString	short_desc;
    double	amount;
    int		type;
    int		form;
    double	min_temperature;
    double	max_temperature;
    int		flocculation;
    int		max_reuse;
    double	attenuation;
    double	cells;
    double	tolerance;
    int		use;
    bool	sta1;
    bool	bacteria;
    bool	harvest_top;
    int		harvest_time;
    double	pitch_temperature;
    bool	pofpos;
    int		zymocide;
    int		gr_hl_lo;
    double	sg_lo;
    int		gr_hl_hi;
    double	sg_hi;
    double	cost;
    double	inventory;		///< In product, current inventory.
    bool	avail;			///< Product available in database.
};


struct MashSteps
{
    QString	step_name;
    int		step_type;
    double	step_volume;		///< The water volume upto this step.
    double	step_infuse_amount;	///< Infuse/decoction volume this step.
    double	step_infuse_temp;	///< Infuse/decoction temperature.
    double	step_temp;		///< Start temperature this step.
    double	step_time;		///< Step rest time.
    double	ramp_time;		///< Estimated ramp time to this step.
    double	end_temp;		///< End temperature this step.
    double	step_wg_ratio;		///< Current water/grain ratio.
    double	step_ph;		///< In product, measured pH.
    double	step_sg;		///< In product, measured SG.
};


struct Mashs
{
    QString		name;
    QString		notes;
    QJsonDocument	steps;
};


struct Waters
{
    QString	name;
    QString	notes;
    bool	unlimited_stock;
    double	calcium;
    double	bicarbonate;
    double	total_alkalinity;
    double	sulfate;
    double	chloride;
    double	sodium;
    double	magnesium;
    double	nitrate;
    double	ph;
    double	cost;
    double	inventory;
    bool	avail;
};


struct Splits
{
    QString	name;			///< Name of split part
    QString	code;			///< Code of split part
    double	size;			///< Split volume size
};


struct Images
{
    int		pic_type;
    QByteArray	pic_data;
    QString	pic_comment;
    QString	filename;
    QDateTime	timestamp;
};


struct Equipment
{
    QString	name;
    double	boil_size;
    double	batch_size;
    double	tun_volume;
    double	tun_weight;
    double	tun_specific_heat;
    int		tun_material;
    double	tun_height;
    double	top_up_water;
    double	trub_loss;
    double	evap_rate;
    double	boil_time;
    bool	x_calc_boil_volume;
    double	top_up_kettle;
    double	hop_utilization;
    QString	notes;
    double	lauter_volume;
    double	lauter_height;
    double	lauter_deadspace;
    double	kettle_volume;
    double	kettle_height;
    double	mash_volume;
    double	mash_max;
    double	efficiency;
    QString	uuid;
};


struct Style
{
    QString	name;
    QString	category;
    int		category_number;
    QString	style_letter;
    QString	style_guide;
    int		type;
    double	og_min;
    double	og_max;
    double	fg_min;
    double	fg_max;
    double	ibu_min;
    double	ibu_max;
    double	color_min;
    double	color_max;
    double	carb_min;
    double	carb_max;
    double	abv_min;
    double	abv_max;
    QString	notes;
    QString	profile;
    QString	ingredients;
    QString	examples;
    QString	uuid;
};


/*
 * The main recipe record stored in the database.
 */
struct Recipe
{
    int		record;
    QString	uuid;
    bool	locked;
    QString	st_name;
    QString	st_letter;
    QString	st_guide;
    QString	st_category;
    int		st_category_number;
    int		st_type;
    double	st_og_min;
    double	st_og_max;
    double	st_fg_min;
    double	st_fg_max;
    double	st_ibu_min;
    double	st_ibu_max;
    double	st_color_min;
    double	st_color_max;
    double	st_carb_min;
    double	st_carb_max;
    double	st_abv_min;
    double	st_abv_max;

    QString	name;
    QString	notes;
    int		type;
    double	batch_size;
    double	boil_size;
    double	boil_time;
    double	efficiency;
    double	est_og;
    double	est_fg;
    double	est_abv;
    double	est_color;
    int		color_method;
    double	est_ibu;
    int		ibu_method;
    double	est_carb;

    double	sparge_temp;
    double	sparge_ph;
    double	sparge_volume;
    int		sparge_source;
    int		sparge_acid_type;
    double	sparge_acid_perc;
    double	sparge_acid_amount;
    double	mash_ph;
    QString	mash_name;
    bool	calc_acid;

    QString	w1_name;		///< Water source 1
    double	w1_amount;
    double	w1_calcium;
    double	w1_sulfate;
    double	w1_chloride;
    double	w1_sodium;
    double	w1_magnesium;
    double	w1_total_alkalinity;
    double	w1_ph;
    double	w1_cost;
    QString     w2_name;		///< Water source 2
    double      w2_amount;
    double      w2_calcium;
    double      w2_sulfate;
    double      w2_chloride;
    double      w2_sodium;
    double      w2_magnesium;
    double      w2_total_alkalinity;
    double      w2_ph;
    double      w2_cost;
    double      wg_amount;		///< Mixed water
    double      wg_calcium;
    double      wg_sulfate;
    double      wg_chloride;
    double      wg_sodium;
    double      wg_magnesium;
    double      wg_total_alkalinity;
    double      wg_ph;
    double      wb_calcium;		///< Treated water
    double      wb_sulfate;
    double      wb_chloride;
    double      wb_sodium;
    double      wb_magnesium;
    double      wb_total_alkalinity;
    double      wb_ph;
    int		wa_acid_name;
    double	wa_acid_perc;
    int		wa_base_name;

    QList<Fermentables>	fermentables;
    QList<Hops>		hops;
    QList<Miscs>	miscs;
    QList<Yeasts>	yeasts;
    QList<MashSteps>	mashs;

    /*
     * These are not in the MySL database, but are global variables
     * that belong with the loaded recipe data and are present to
     * make things easier.
     */
    int		fermentables_row;	///< Current row, -1 is invalid.
    bool	fermentables_use100;	///< Use percentages instead of amount
    int		hops_row;
    int		miscs_row;
    int		yeasts_row;
    int		mashs_row;
    double	mashs_kg;		///< Kg fermentables in the mash.
    int		mashs_time;		///< Total mash time.
    double	preboil_sg;
    double      preboil_ph;
    double	ws_calcium;		///< Sparge water calculated.
    double      ws_sulfate;
    double      ws_chloride;
    double      ws_sodium;
    double      ws_magnesium;
    double      ws_total_alkalinity;
};


/*
 * The main product record stored in the database.
 */
struct Product
{
    int		record;
    QString	uuid;
    QString	name;
    QString	code;
    QDate	birth;
    int		stage;
    QString     notes;
    bool	log_brew;
    bool	log_fermentation;
    bool	log_ispindel;
    bool	log_co2pressure;
    int		inventory_reduced;
    bool	locked;

    QString	eq_name;
    QString	eq_notes;
    double	eq_boil_size;
    double	eq_batch_size;
    double	eq_tun_volume;
    double	eq_tun_weight;
    double	eq_tun_specific_heat;
    int		eq_tun_material;
    double	eq_tun_height;
    double	eq_top_up_water;
    double	eq_trub_loss;
    double	eq_evap_rate;
    double	eq_boil_time;
    bool	xeq_calc_boil_volume;
    double	eq_top_up_kettle;
    double	xeq_hop_utilization;
    double	xeq_lauter_volume;
    double	xeq_lauter_height;
    double	eq_lauter_deadspace;
    double	eq_kettle_volume;
    double	eq_kettle_height;
    double	eq_mash_volume;
    double	eq_mash_max;
    double	eq_efficiency;
    int		eq_chiller_type;
    double	eq_chiller_to79;
    double	eq_chiller_volume;
    double	eq_chiller_lpm;
    double	eq_chiller_loss;

    QDateTime	brew_date_start;
    double	brew_mash_ph;
    double	brew_mash_sg;
    double	brew_mash_efficiency;
    double	brew_sparge_temperature;
    double	brew_sparge_volume;
    double	brew_sparge_est;
    double	brew_sparge_ph;
    double	brew_preboil_volume;
    double	brew_preboil_sg;
    double	brew_preboil_ph;
    double	brew_preboil_efficiency;
    double	brew_aboil_volume;
    double	brew_aboil_sg;
    double	brew_aboil_ph;
    double	brew_aboil_efficiency;
    int		brew_cooling_method;
    double	brew_cooling_time;
    double	brew_cooling_to;
    double	brew_whirlpool9;
    double	brew_whirlpool7;
    double	brew_whirlpool6;
    double	brew_whirlpool2;
    double	brew_fermenter_volume;
    double	brew_fermenter_extrawater;
    double	brew_fermenter_tcloss;
    double	brew_aeration_time;
    double	brew_aeration_speed;
    int		brew_aeration_type;
    double	brew_fermenter_sg;
    double	brew_fermenter_ibu;
    double	brew_fermenter_color;
    QDateTime	brew_date_end;

    double	og;
    double	fg;
    double	primary_start_temp;
    double	primary_max_temp;
    double	primary_end_temp;
    double	primary_end_sg;
    QDate	primary_end_date;
    double	secondary_temp;
    double	secondary_end_sg;
    QDate	secondary_end_date;
    double	tertiary_temp;

    QDate	package_date;
    double	package_volume;
    double	package_infuse_amount;
    double	package_infuse_abv;
    QString	package_infuse_notes;
    double	package_abv;
    double	package_ph;
    double	bottle_amount;
    double	bottle_carbonation;
    int		bottle_priming_sugar;
    double	bottle_priming_amount;
    double	bottle_priming_water;
    double	bottle_carbonation_temp;
    double	keg_amount;
    double	keg_carbonation;
    int		keg_priming_sugar;
    double	keg_priming_amount;
    double	keg_priming_water;
    double	keg_carbonation_temp;
    bool	keg_forced_carb;
    double	keg_pressure;

    QString	taste_notes;
    double	taste_rate;
    QDate	taste_date;
    QString	taste_color;
    QString	taste_transparency;
    QString	taste_head;
    QString	taste_aroma;
    QString	taste_taste;
    QString	taste_mouthfeel;
    QString	taste_aftertaste;

    QString	st_name;
    QString	st_letter;
    QString	st_guide;
    QString	st_category;
    int		st_category_number;
    int		st_type;
    double	st_og_min;
    double	st_og_max;
    double	st_fg_min;
    double	st_fg_max;
    double	st_ibu_min;
    double	st_ibu_max;
    double	st_color_min;
    double	st_color_max;
    double	st_carb_min;
    double	st_carb_max;
    double	st_abv_min;
    double	st_abv_max;

    int		type;
    double	batch_size;
    double	boil_size;
    double	boil_time;
    double	efficiency;
    double	est_og;
    double	est_og3;
    double	est_fg;
    double	est_abv;
    double	est_color;
    int		color_method;
    double	est_ibu;
    int		ibu_method;
    double	est_carb;

    double	sparge_temp;
    double	sparge_ph;
    double	sparge_volume;
    int		sparge_source;
    int		sparge_acid_type;
    double	sparge_acid_perc;
    double	sparge_acid_amount;
    double	mash_ph;
    QString	mash_name;
    bool	calc_acid;

    QString	w1_name;		///< Water source 1
    double	w1_amount;
    double	w1_calcium;
    double	w1_sulfate;
    double	w1_chloride;
    double	w1_sodium;
    double	w1_magnesium;
    double	w1_total_alkalinity;
    double	w1_ph;
    double	w1_cost;
    QString     w2_name;		///< Water source 2
    double      w2_amount;
    double      w2_calcium;
    double      w2_sulfate;
    double      w2_chloride;
    double      w2_sodium;
    double      w2_magnesium;
    double      w2_total_alkalinity;
    double      w2_ph;
    double      w2_cost;
    double      wg_amount;		///< Mixed water
    double      wg_calcium;
    double      wg_sulfate;
    double      wg_chloride;
    double      wg_sodium;
    double      wg_magnesium;
    double      wg_total_alkalinity;
    double      wg_ph;
    double      wb_calcium;		///< Treated water
    double      wb_sulfate;
    double      wb_chloride;
    double      wb_sodium;
    double      wb_magnesium;
    double      wb_total_alkalinity;
    double      wb_ph;
    int		wa_acid_name;
    double	wa_acid_perc;
    int		wa_base_name;

    bool	starter_enable;
    int		starter_type;
    double	starter_sg;
    int		starter_viability;
    QDate	yeast_prod_date;
    double	yeast_pitchrate;
    int		prop_type[4];
    double	prop_volume[4];

    int		divide_type;
    double	divide_size;
    double	divide_factor;
    int		divide_parts;
    int		divide_part;

    QList<Fermentables>	fermentables;
    QList<Hops>		hops;
    QList<Miscs>	miscs;
    QList<Yeasts>	yeasts;
    QList<MashSteps>	mashs;

    /*
     * These are not in the MySL database, but are global variables
     * that belong with the loaded product data and are present to
     * make things easier.
     */
    int		fermentables_row;	///< Current row, -1 is invalid.
    bool	fermentables_use100;	///< Use percentages instead of amount
    bool	fermentables_ok;	///< Inventory check
    int		hops_row;
    bool	hops_ok;
    double	boil_absorb;		///< Absorption in L of hops in the boil kettle.
    double	ferment_absorb;		///< Absorption in L caused by dry-hops.
    int		miscs_row;
    bool	miscs_ok;
    int		yeasts_row;
    bool	yeasts_ok;
    int		pitch_pitchindex;
    bool	waters_ok;
    int		mashs_row;
    double	mashs_kg;		///< Kg fermentables in the mash.
    int		mashs_time;		///< Total mash time.
    double	est_mash_sg;
    double	est_preboil_ph;
    double	preboil_sg;
    double	final_abv;		///< ABV after dilution/infusion.
    double	bottle_abv;
    double	bottle_bar;
    double	keg_abv;
    double	keg_bar;
    QList<Splits>	splits;		///< Used during building a split batch
    double      ws_calcium;             ///< Sparge water calculated.
    double      ws_sulfate;
    double      ws_chloride;
    double      ws_sodium;
    double      ws_magnesium;
    double      ws_total_alkalinity;
    QList<Images>	images_list;	///< List of loaded images.
    int		images_count;		///< -1 if not yet loaded.
    int		images_current;		///< -1 or image in focus.
    bool	images_dirty;
};


extern Recipe		*recipe;
extern Product		*product;

extern QString		my_brewery_name;
extern QByteArray	my_logoByteArray;
extern int		my_factor_mashhop;
extern int		my_factor_fwh;
extern int		my_ibu_method;
extern int		my_color_method;
extern double		my_brix_correction;
extern double		my_grain_absorption;
extern int		my_default_water;
extern QString		my_yeastlab;
extern int		my_height;
extern double		my_ut_pellet;
extern double		my_ut_plug;
extern double		my_ut_leaf;
extern double		my_ut_wethop;
extern double		my_ut_t45;
extern double		my_ut_co2extract;
extern double		my_ha_pellet;
extern double		my_ha_plug;
extern double		my_ha_leaf;
extern double		my_ha_wethop;
extern double		my_ha_t45;


enum ProdStages {
	PROD_STAGE_PLAN,
	PROD_STAGE_WAIT,
	PROD_STAGE_BREW,
	PROD_STAGE_PRIMARY,
	PROD_STAGE_SECONDARY,
	PROD_STAGE_TERTIARY,
	PROD_STAGE_PACKAGE,
	PROD_STAGE_CARBONATION,
	PROD_STAGE_MATURE,
	PROD_STAGE_TASTE,
	PROD_STAGE_READY,
	PROD_STAGE_CLOSED
};

extern const char * const g_prod_stages[];
extern const char * const g_prod_split[];
extern const char * const g_prod_pic_types[];
extern const char * const g_recipe_types[];
extern const char * const g_style_types[];
extern const char * const g_chiller_types[];

enum ChillerTypes {
	CHILLER_TYPE_NONE,
	CHILLER_TYPE_IMMERSION,
	CHILLER_TYPE_COUNTERFLOW,
	CHILLER_TYPE_AUBAINMARIE,
	CHILLER_TYPE_NOCHILL
};

extern const char * g_ibu_method[3];

enum FermentableTypes {
	FERMENTABLE_TYPE_GRAIN,
	FERMENTABLE_TYPE_SUGAR,
	FERMENTABLE_TYPE_EXTRACT,
	FERMENTABLE_TYPE_DRY_EXTRACT,
	FERMENTABLE_TYPE_ADJUCT
};

extern const char * const g_fermentable_types[];

enum FermentableGraintypes {
	FERMENTABLE_GRAINTYPE_BASE,
	FERMENTABLE_GRAINTYPE_ROAST,
	FERMENTABLE_GRAINTYPE_CRYSTAL,
	FERMENTABLE_GRAINTYPE_KILNED,
	FERMENTABLE_GRAINTYPE_SOUR_MALT,
	FERMENTABLE_GRAINTYPE_SPECIAL,
	FERMENTABLE_GRAINTYPE_NO_MALT
};

extern const char * const g_fermentable_graintypes[];

enum FermentableAdded {
	FERMENTABLE_ADDED_MASH,
	FERMENTABLE_ADDED_BOIL,
	FERMENTABLE_ADDED_FERMENTATION,
	FERMENTABLE_ADDED_LAGERING,
	FERMENTABLE_ADDED_BOTTLE,
	FERMENTABLE_ADDED_KEGS
};

extern const char * const g_fermentable_added[];

enum HopTypes {
	HOP_TYPE_BITTERING,
	HOP_TYPE_AROMA,
	HOP_TYPE_BOTH
};

extern const char * const g_hop_types[];

enum HopForms {
	HOP_FORMS_PELLET,			///< T-90 pellets
	HOP_FORMS_PLUG,
	HOP_FORMS_LEAF,				///< Ordinary leafs
	HOP_FORMS_LEAF_WET,			///< Fresh picked leafs
	HOP_FORMS_CRYO,				///< Cryo T-45 hops.
	HOP_FORMS_CO2EXTRACT,			///< CO2 extract and IKE.
	HOP_FORMS_ISOEXTRACT			///< Isomerized Hop Extract.
};

extern const char * const g_hop_forms[];

enum HopUseat {
	HOP_USEAT_MASH,
	HOP_USEAT_FWH,
	HOP_USEAT_BOIL,
	HOP_USEAT_AROMA,
	HOP_USEAT_WHIRLPOOL,
	HOP_USEAT_DRY_HOP,
	HOP_USEAT_BOTTLING
};

extern const char * const g_hop_useat[];

enum MiscTypes {
	MISC_TYPES_SPICE,
	MISC_TYPES_HERB,
	MISC_TYPES_FLAVOR,
	MISC_TYPES_FINING,
	MISC_TYPES_WATER_AGENT,
	MISC_TYPES_YEAST_NUTRIENT,
	MISC_TYPES_OTHER
};

extern const char * const g_misc_types[];

enum MiscUses {
	MISC_USES_STARTER,
	MISC_USES_MASH,
	MISC_USES_BOIL,
	MISC_USES_PRIMARY,
	MISC_USES_SECONDARY,
	MISC_USES_BOTTLING,
	MISC_USES_SPARGE
};

extern const char * const g_misc_uses[];

enum YeastTypes {
	YEAST_TYPES_LAGER,
	YEAST_TYPES_ALE,
	YEAST_TYPES_WHEAT,
	YEAST_TYPES_WINE,
	YEAST_TYPES_CHAMPAGNE,
	YEAST_TYPES_BRETT,
	YEAST_TYPES_KVEIK,
	YEAST_TYPES_HYBRID
};

extern const char * const g_yeast_types[];

enum YeastForms {
	YEAST_FORMS_LIQUID,
	YEAST_FORMS_DRY,
	YEAST_FORMS_SLANT,
	YEAST_FORMS_CULTURE,
	YEAST_FORMS_FROZEN,
	YEAST_FORMS_BOTTLE,
	YEAST_FORMS_DRIED
};

extern const char * const g_yeast_forms[];

enum YeastUse {
	YEAST_USE_PRIMARY,
	YEAST_USE_SECONDARY,
	YEAST_USE_TERTIARY,
	YEAST_USE_BOTTLE
};

extern const char * const g_yeast_use[];

enum Starters {
	STARTERS_STIRRED,
	STARTERS_SHAKEN,
	STARTERS_SIMPLE
};

extern const char * const g_yeast_starter[];
extern const char * const g_step_types[];
extern const char * const g_tun_materials[];

#endif

mercurial