//
// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
//
// The contents of this file are covered under the licence agreement in the
// file "LICENCE" distributed with Cforall.
//
// Cost.h --
//
// Author           : Peter Buhr and Aaron Moss
// Created On       : Sun May 17 09:39:50 2015
// Last Modified By : Peter A. Buhr
// Last Modified On : Mon Apr 29 18:33:44 2019
// Update Count     : 49
//

#pragma once

#include <iostream>
#include <cassert>
#include <climits>

namespace ResolvExpr {
#if 0

	//*************************** OLD ***************************

	class Cost {
	  private:
		Cost( int unsafeCost, int polyCost, int safeCost, int signCost,
			  int varCost, int specCost, int referenceCost );
	  public:
		Cost & incUnsafe( int inc = 1 );
		Cost & incPoly( int inc = 1 );
		Cost & incSafe( int inc = 1 );
		Cost & incSign( int inc = 1 );
		Cost & incVar( int inc = 1 );
		Cost & decSpec( int inc = 1 );
		Cost & incReference( int inc = 1 );

		int get_unsafeCost() const { return unsafeCost; }
		int get_polyCost() const { return polyCost; }
		int get_safeCost() const { return safeCost; }
		int get_signCost() const { return signCost; }
		int get_varCost() const { return varCost; }
		int get_specCost() const { return specCost; }
		int get_referenceCost() const { return referenceCost; }

		Cost operator+( const Cost &other ) const;
		Cost &operator+=( const Cost &other );
		bool operator<( const Cost &other ) const;
		bool operator==( const Cost &other ) const;
		bool operator!=( const Cost &other ) const;
		friend std::ostream &operator<<( std::ostream &os, const Cost &cost );
		// returns negative for *this < other, 0 for *this == other, positive for *this > other
		int compare( const Cost &other ) const;

		static const Cost zero;
		static const Cost infinity;

		static const Cost unsafe;
		static const Cost poly;
		static const Cost safe;
		static const Cost sign;
		static const Cost var;
		static const Cost spec;
		static const Cost reference;

	  private:
		int unsafeCost;     ///< Unsafe (narrowing) conversions
		int polyCost;       ///< Count of parameters and return values bound to some poly type
		int safeCost;       ///< Safe (widening) conversions
		int signCost;       ///< Count of safe sign conversions
		int varCost;        ///< Count of polymorphic type variables
		int specCost;       ///< Polymorphic type specializations (type assertions), negative cost
		int referenceCost;  ///< reference conversions
	};

	inline Cost::Cost( int unsafeCost, int polyCost, int safeCost, int signCost,
					   int varCost, int specCost, int referenceCost )
		: unsafeCost( unsafeCost ), polyCost( polyCost ), safeCost( safeCost ), signCost( signCost ),
		  varCost( varCost ), specCost( specCost ), referenceCost( referenceCost ) {}

	inline Cost & Cost::incUnsafe( int inc ) {
		if ( *this == infinity ) return *this;
		unsafeCost += inc;
		return *this;
	}

	inline Cost & Cost::incPoly( int inc ) {
		if ( *this == infinity ) return *this;
		polyCost += inc;
		return *this;
	}

	inline Cost & Cost::incSafe( int inc ) {
		if ( *this == infinity ) return *this;
		safeCost += inc;
		return *this;
	}

	inline Cost & Cost::incSign( int inc ) {
		if ( *this == infinity ) return *this;
		signCost += inc;
		return *this;
	}

	inline Cost & Cost::incVar( int inc ) {
		if ( *this == infinity ) return *this;
		varCost += inc;
		return *this;
	}

	inline Cost& Cost::decSpec( int dec ) {
		if ( *this == infinity ) return *this;
		specCost -= dec;
		return *this;
	}

	inline Cost & Cost::incReference( int inc ) {
		if ( *this == infinity ) return *this;
		referenceCost += inc;
		return *this;
	}

	inline Cost Cost::operator+( const Cost &other ) const {
		if ( *this == infinity || other == infinity ) return infinity;
		return Cost{
			unsafeCost + other.unsafeCost, polyCost + other.polyCost, safeCost + other.safeCost,
				signCost + other.signCost, varCost + other.varCost, specCost + other.specCost,
				referenceCost + other.referenceCost };
	}

	inline Cost &Cost::operator+=( const Cost &other ) {
		if ( *this == infinity ) return *this;
		if ( other == infinity ) {
			*this = infinity;
			return *this;
		}
		unsafeCost += other.unsafeCost;
		polyCost += other.polyCost;
		safeCost += other.safeCost;
		signCost += other.signCost;
		varCost += other.varCost;
		specCost += other.specCost;
		referenceCost += other.referenceCost;
		return *this;
	}

	inline bool Cost::operator<( const Cost &other ) const {
		if ( *this == infinity ) return false;
		if ( other == infinity ) return true;

		if ( unsafeCost > other.unsafeCost ) {
			return false;
		} else if ( unsafeCost < other.unsafeCost ) {
			return true;
		} else if ( polyCost > other.polyCost ) {
			return false;
		} else if ( polyCost < other.polyCost ) {
			return true;
		} else if ( safeCost > other.safeCost ) {
			return false;
		} else if ( safeCost < other.safeCost ) {
			return true;
		} else if ( signCost > other.signCost ) {
			return false;
		} else if ( signCost < other.signCost ) {
			return true;
		} else if ( varCost > other.varCost ) {
			return false;
		} else if ( varCost < other.varCost ) {
			return true;
		} else if ( specCost > other.specCost ) {
			return false;
		} else if ( specCost > other.specCost ) {
			return true;
		} else if ( referenceCost > other.referenceCost ) {
			return false;
		} else if ( referenceCost < other.referenceCost ) {
			return true;
		} else {
			return false;
		} // if
	}

	inline int Cost::compare( const Cost &other ) const {
		if ( *this == infinity ) return +1;
		if ( other == infinity ) return -1;

		int c = unsafeCost - other.unsafeCost; if ( c ) return c;
		c = polyCost - other.polyCost; if ( c ) return c;
		c = safeCost - other.safeCost; if ( c ) return c;
		c = signCost - other.signCost; if ( c ) return c;
		c = varCost - other.varCost; if ( c ) return c;
		c = specCost - other.specCost; if ( c ) return c;
		return referenceCost - other.referenceCost;
	}

	inline bool Cost::operator==( const Cost &other ) const {
		return unsafeCost == other.unsafeCost
			&& polyCost == other.polyCost
			&& safeCost == other.safeCost
			&& signCost == other.signCost
			&& varCost == other.varCost
			&& specCost == other.specCost
			&& referenceCost == other.referenceCost;
	}

	inline bool Cost::operator!=( const Cost &other ) const {
		return !( *this == other );
	}

	inline std::ostream &operator<<( std::ostream &os, const Cost &cost ) {
		return os << "( " << cost.unsafeCost << ", " << cost.polyCost << ", "
		          << cost.safeCost << ", " << cost.signCost << ", "
				  << cost.varCost << ", " << cost.specCost << ", "
		          << cost.referenceCost << " )";
	}

#else

	//*************************** NEW ***************************

	// To maximize performance and space, the 7 resolution costs are packed into a single 64-bit word. However, the
	// specialization cost is a negative value so a correction is needed is a few places.

	class Cost {
		union {
			struct {
			#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
				// Little-endian => first value is low priority and last is high priority.
				unsigned char padding;					///< unused
				unsigned char referenceCost;			///< reference conversions
				unsigned char specCost;					///< Polymorphic type specializations (type assertions), negative cost
				unsigned char varCost;					///< Count of polymorphic type variables
				unsigned char signCost;					///< Count of safe sign conversions
				unsigned char safeCost;					///< Safe (widening) conversions
				unsigned char polyCost;					///< Count of parameters and return values bound to some poly type
				unsigned char unsafeCost;				///< Unsafe (narrowing) conversions
			#else
				#error Cost BIG_ENDIAN unsupported
			#endif
			} v;
			uint64_t all;
		};
		static const unsigned char correctb = 0xff;		// byte correction for negative spec cost
		static const uint64_t correctw = 0x00'00'00'00'00'ff'00'00; //' word correction for negative spec cost
	  public:
		// Compiler adjusts constants for correct endian.
		enum : uint64_t {
			zero      = 0x00'00'00'00'00'ff'00'00,
			infinity  = 0xff'ff'ff'ff'ff'00'ff'ff,
			unsafe    = 0x01'00'00'00'00'ff'00'00,
			poly      = 0x00'01'00'00'00'ff'00'00,
			safe      = 0x00'00'01'00'00'ff'00'00,
			sign      = 0x00'00'00'01'00'ff'00'00,
			var       = 0x00'00'00'00'01'ff'00'00,
			spec      = 0x00'00'00'00'00'fe'00'00,
			reference = 0x00'00'00'00'00'ff'01'00,
		}; //'

		Cost( uint64_t all ) { Cost::all = all; }
		Cost( int unsafeCost, int polyCost, int safeCost, int signCost, int varCost, int specCost, int referenceCost ) {
			// Assume little-endian => first value is low priority and last is high priority.
			v = {
			#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
				(unsigned char)0,						// padding
				(unsigned char)referenceCost,			// low priority
				(unsigned char)(specCost + correctb),	// correct for signedness
				(unsigned char)varCost, 
				(unsigned char)signCost, 
				(unsigned char)safeCost, 
				(unsigned char)polyCost, 
				(unsigned char)unsafeCost, 				// high priority
			#else
				#error Cost BIG_ENDIAN unsupported
			#endif
			};
		}

		int get_unsafeCost() const { return v.unsafeCost; }
		int get_polyCost() const { return v.polyCost; }
		int get_safeCost() const { return v.safeCost; }
		int get_signCost() const { return v.signCost; }
		int get_varCost() const { return v.varCost; }
		int get_specCost() const { return -(correctb - v.specCost); }
		int get_referenceCost() const { return v.referenceCost; }

		friend bool operator==( const Cost, const Cost );
		friend bool operator!=( const Cost lhs, const Cost rhs );
		// returns negative for *this < rhs, 0 for *this == rhs, positive for *this > rhs
		int compare( const Cost rhs ) const {
			if ( all == infinity ) return 1;
			if ( rhs.all == infinity ) return -1;
			return all > rhs.all ? 1 : all == rhs.all ? 0 : -1;
		}
		friend bool operator<( const Cost lhs, const Cost rhs );

		friend Cost operator+( const Cost lhs, const Cost rhs );
 
		Cost operator+=( const Cost rhs ) {
			if ( all == infinity ) return *this;
			if ( rhs.all == infinity ) {
				all = infinity;
				return *this;
			}
			all += rhs.all - correctw;					// correct for negative spec cost
			return *this;
		}

		Cost incUnsafe( int inc = 1 ) {
			if ( all != infinity ) { assert( v.unsafeCost + inc <= UCHAR_MAX ); v.unsafeCost += inc; }
			return *this;
		}

		Cost incPoly( int inc = 1 ) {
			if ( all != infinity ) { assert( v.polyCost + inc <= UCHAR_MAX ); v.polyCost += inc; }
			return *this;
		}

		Cost incSafe( int inc = 1 ) {
			if ( all != infinity ) { assert( v.safeCost + inc <= UCHAR_MAX ); v.safeCost += inc; }
			return *this;
		}

		Cost incSign( int inc = 1 ) {
			if ( all != infinity ) { assert( v.signCost + inc <= UCHAR_MAX ); v.signCost += inc; }
			return *this;
		}

		Cost incVar( int inc = 1 ) {
			if ( all != infinity ) { assert( v.varCost + inc <= UCHAR_MAX ); v.varCost += inc; }
			return *this;
		}

		Cost decSpec( int dec = 1 ) {
			if ( all != infinity ) { assert( v.specCost - dec >= 0 ); v.specCost -= dec; }
			return *this;
		}

		Cost incReference( int inc = 1 ) {
			if ( all != infinity ) { assert( v.referenceCost + inc <= UCHAR_MAX ); v.referenceCost += inc; }
			return *this;
		}

		friend std::ostream & operator<<( std::ostream & os, const Cost cost );
	};

	inline bool operator==( const Cost lhs, const Cost rhs ) {
		return lhs.all == rhs.all;
	}

	inline bool operator!=( const Cost lhs, const Cost rhs ) {
		return !( lhs.all == rhs.all );
	}

	inline bool operator<( const Cost lhs, const Cost rhs ) {
		if ( lhs.all == Cost::infinity ) return false;
		if ( rhs.all == Cost::infinity ) return true;
		return lhs.all < rhs.all;
	}

	inline Cost operator+( const Cost lhs, const Cost rhs ) {
		if ( lhs.all == Cost::infinity || rhs.all == Cost::infinity ) return Cost{ Cost::infinity };
		return Cost{ lhs.all + rhs.all - Cost::correctw }; // correct for negative spec cost
	}

	inline std::ostream & operator<<( std::ostream & os, const Cost cost ) {
		return os << "( " << cost.get_unsafeCost() << ", " << cost.get_polyCost() << ", " << cost.get_safeCost()
				  << ", " << cost.get_signCost() << ", " << cost.get_varCost() << ", " << cost.get_specCost()
				  << ", " << cost.get_referenceCost() << " )";
	}
#endif // 0
} // namespace ResolvExpr

// Local Variables: //
// tab-width: 4 //
// mode: c++ //
// compile-command: "make install" //
// End: //
