#region << Using Directives >>
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Volpe.Cafe.Data;
using Volpe.Cafe.IO;
using Volpe.Cafe.Settings;
using Volpe.Cafe.Utils;
using TI = Volpe.Cafe.TechnologyIndexes;
using TA = Volpe.Cafe.Model.TechnologyApplicability;
using Volpe.Cafe.Generic;
#endregion
namespace Volpe.Cafe.Model
{
    [Serializable]
    public class ComplianceFinding
    {
        #region 
        public ComplianceFinding()
        {
            this._capacity                 = 8;
            this._vehs                     = new Vehicle   [this._capacity];
            this._ignore                   = new int       [this._capacity];
            this._techKeys                 = new ulong     [this._capacity];
            this._techLingerKeys           = new ulong     [this._capacity];
            this._lingerYear               = new int       [this._capacity];
            this._lingering                = new bool      [this._capacity];
            this._techImpacts              = new TechImpact[this._capacity];
            this._yearOfInitialApplication = new int       [this._capacity][];
            this._committedERPCosts        = new bool      [TI.TechnologyCount];
            this._regClasses               = new List<RegulatoryClass>();
            this.Reset();
        }
        #endregion
        #region 
        #region 
        public override string ToString()
        {
            bool valid = this.IsValid;
            if (valid)
            {
                Manufacturer.CModelData mmd = this._vehs[0].Manufacturer.ModelData;
                string s = 
                    "ComplianceFinding # " + this._index +
                    ": {\nIsValid: true\nIsEfficient: "  + this.IsEfficient +
                    "\nEfficiency: "    + this._efficiency         +
                    "\nCostPerMpg: "    + this._costPerMPG         +
                    "\nTechCost: "      + this._techCost           +
                    "\nRPECost: "       + this._totalDeltaRPE      +
                    "\nDeltaFines: "    + this._totalDeltaFines    +
                    "\nDeltaCredits: "  + this._totalDeltaCredits  +
                    "\nFuelSavings: "   + this._totalFuelSavings   +
                    "\nAffectedSales: " + this._totalAffectedSales +
                    "\nCAFE (old, new): {";
                for (int i = 0; i < RCValue<object>.Classes.Length; i++)
                {
                    s += "\n\t  " + RCValue<object>.Names[i] + ": " + this._curCafe.Items[i] + ", " + this._newCafe.Items[i];
                }
                s += "\n\t}\nFines (old, new): {";
                for (int i = 0; i < RCValue<object>.Classes.Length; i++)
                {
                    s += "\n\t  " + RCValue<object>.Names[i] + ": " + this._curFines.Items[i] + ", " + this._newFines.Items[i];
                }
                s += "\n\t}\n}";
                return s;
            }
            else { return "ComplianceFinding # " + this._index + ": {\nValid: false}"; }
        }
        #endregion
        #region 
        public void Initialize(Scenario scen, ModelYear year, int startYear, int endYear, Industry[] modelYearData, int mfrIndex,
            ModelingSettings settings, LogWriter logWriter, TechnologyType techType, List<Technology> techGroup)
        {
            this._scen           = scen;
            this._year           = year;
            this._yrIndex        = year.Index;
            this._yrYear         = year.Year;
            this._startYear      = startYear;
            this._endYear        = endYear;
            this._modelYearData  = modelYearData;
            this._mfrIndex       = mfrIndex;
            this._settings       = settings;
            this._parameters     = settings.Parameters;
            this._overcomply     = false;
            this._creditTrade    = false;
            this._multiYear      = settings.OperatingModes.MultiYearModeling;
            this._ignorePhaseIn  = settings.OperatingModes.IgnorePhaseIn;
            this._techInheriting = settings.OperatingModes.TechnologyInheriting;
            this._backfill       = settings.OperatingModes.Backfill;
            this._logWriter      = logWriter;
            this._techType       = techType;
            this._techGroup      = techGroup;
            this._techCount      = techGroup.Count;
            this._doInherit      = false;
            this.Reset();
        }
        public void Reset()
        {
            this._index              = -1;
            this._vehCount           = 0;
            this._techIndex          = 0;
            this._minLingerYear      = 0;
            this._hasVehs            = false;
            this._isRcMix            = false;
            this._isEnablesMix       = false;
            this._isSplitCf          = false;
            this._isApplied          = false;
            this._efficiency         = double.NaN;
            this._techCost           = double.NaN;
            this._newCafe            = RCDouble.Zero;
            this._newFines           = RCDouble.Zero;
            this._effectiveNewFines  = RCDouble.Zero;
            this._curCafe            = RCDouble.Zero;
            this._curFines           = RCDouble.Zero;
            this._costPerMPG         = 0;
            this._totalDeltaRPE      = 0;
            this._totalDeltaFines    = 0;
            this._totalDeltaCredits  = 0;
            this._totalFuelSavings   = 0;
            this._totalAffectedSales = 0;
            this._regClasses.Clear();
        }
        public void Clear()
        {
            this._scen           = null;
            this._year           = null;
            this._yrIndex        = 0;
            this._yrYear         = 0;
            this._startYear      = 0;
            this._endYear        = 0;
            this._modelYearData  = null;
            this._mfrIndex       = 0;
            this._settings       = null;
            this._parameters     = null;
            this._overcomply     = false;
            this._creditTrade    = false;
            this._multiYear      = false;
            this._ignorePhaseIn  = false;
            this._techInheriting = false;
            this._backfill       = false;
            this._logWriter      = LogWriter.Empty;
            this._techType       = TechnologyType.Other;
            this._techGroup      = null;
            this._techCount      = 0;
            this._doInherit      = false;
            this.Reset();
        }
        public void EnsureCapacity(int capacity)
        {
            if (this._capacity < capacity)
            {
                this._capacity = ((capacity / 8) + 1) * 8;
                this.EnsureCapacity();
            }
        }
        void EnsureCapacity()
        {
            if (this._techKeys.Length < this._capacity)
            {
                this._vehs                     = new Vehicle   [this._capacity];
                this._ignore                   = new int       [this._capacity];
                this._techKeys                 = new ulong     [this._capacity];
                this._techLingerKeys           = new ulong     [this._capacity];
                this._lingerYear               = new int       [this._capacity];
                this._lingering                = new bool      [this._capacity];
                this._techImpacts              = new TechImpact[this._capacity];
                this._yearOfInitialApplication = new int       [this._capacity][];
            }
            int impactLen = this._endYear - ModelYear.MinYear + 1;
            for (int i = 0; i < this._capacity; i++)
            {
                if (this._techImpacts[i] == null)
                {
                    this._techImpacts[i] = new TechImpact(impactLen);
                }
                if (this._yearOfInitialApplication[i] == null)
                {
                    this._yearOfInitialApplication[i] = new int[TI.TechnologyCount];
                }
            }
        }
        public void CopyFrom(ComplianceFinding cf)
        {
            this._scen               = cf._scen;
            this._year               = cf._year;
            this._yrIndex            = cf._yrIndex;
            this._yrYear             = cf._yrYear;
            this._startYear          = cf._startYear;
            this._endYear            = cf._endYear;
            this._modelYearData      = cf._modelYearData;   
            this._mfrIndex           = cf._mfrIndex;
            this._settings           = cf._settings;
            this._parameters         = cf._parameters;
            this._overcomply         = cf._overcomply;
            this._creditTrade        = cf._creditTrade;
            this._multiYear          = cf._multiYear;
            this._ignorePhaseIn      = cf._ignorePhaseIn;
            this._techInheriting     = cf._techInheriting;
            this._backfill           = cf._backfill;
            this._logWriter          = cf._logWriter;
            this._techType           = cf._techType;
            this._techGroup          = cf._techGroup;
            this._techCount          = cf._techCount;
            this._index              = cf._index;
            this._vehCount           = cf._vehCount;
            this._capacity           = cf._capacity;
            this._techIndex          = cf._techIndex;
            this._minLingerYear      = cf._minLingerYear;
            this._hasVehs            = cf._hasVehs;
            this._isRcMix            = cf._isRcMix;
            this._isEnablesMix       = cf._isEnablesMix;
            this._isSplitCf          = cf._isSplitCf;
            this._isApplied          = cf._isApplied;
            this._efficiency         = cf._efficiency;
            this._techCost           = cf._techCost;
            this._newCafe            = cf._newCafe;
            this._newFines           = cf._newFines;
            this._effectiveNewFines  = cf._effectiveNewFines;
            this._curCafe            = cf._curCafe;
            this._curFines           = cf._curFines;
            this._costPerMPG         = cf._costPerMPG;
            this._totalDeltaRPE      = cf._totalDeltaRPE;
            this._totalDeltaFines    = cf._totalDeltaFines;
            this._totalDeltaCredits  = cf._totalDeltaCredits;
            this._totalFuelSavings   = cf._totalFuelSavings;
            this._totalAffectedSales = cf._totalAffectedSales;
            for (int i = 0, rcCount = cf._regClasses.Count; i < rcCount; i++)
            {
                this._regClasses.Add(cf._regClasses[i]);
            }
            this.EnsureCapacity();
            Array.Copy(cf._vehs          , 0, this._vehs          , 0, this._vehCount);
            Array.Copy(cf._ignore        , 0, this._ignore        , 0, this._vehCount);
            Array.Copy(cf._techKeys      , 0, this._techKeys      , 0, this._vehCount);
            Array.Copy(cf._techLingerKeys, 0, this._techLingerKeys, 0, this._vehCount);
            Array.Copy(cf._lingerYear    , 0, this._lingerYear    , 0, this._vehCount);
            Array.Copy(cf._lingering     , 0, this._lingering     , 0, this._vehCount);
            for (int i = 0; i < this._vehCount; i++)
            {
                cf._techImpacts[i].CopyTo(this._techImpacts[i]);
                Array.Copy(cf._yearOfInitialApplication[i], 0, this._yearOfInitialApplication[i], 0, TI.TechnologyCount);
            }
        }
        public void ExamineFinding(int techIdx, List<Vehicle> vehs, int vehIdx, ref int cfCount, bool overcomply, bool creditTrade)
        {
            this.Reset();
            this.ParseVehTechsLists(techIdx, vehs, vehIdx);
            this._hasVehs = false;
            for (int i = 0; i < this._vehCount; i++)
            {
                if (this._vehs[i].RegClass != RegulatoryClass.None) { this._hasVehs = true; break; }
            }
            if (!this._hasVehs) { return; }     
            this._overcomply  = overcomply;
            this._creditTrade = creditTrade;
            this._doInherit   = false;
            if (this._isRcMix && !this._techInheriting)
            {   
                this.SplitShared(this._isEnablesMix, ref cfCount);
            }
            else if (this._isEnablesMix && !this._techInheriting)
            {   
                this.SplitMixedEnables(true, ref cfCount);
            }
            else
            {   
                this.CalcEfficiency();
                this._index = cfCount++;
            }
        }
        public void ExamineTechInheriting(int techIdx, List<Vehicle> vehs, int vehIdx)
        {
            this.Reset();
            this._doInherit = true;
            this.ParseVehTechsLists(techIdx, vehs, vehIdx);
            this.CalcEfficiency();
        }
        void ParseVehTechsLists(int techIdx, List<Vehicle> vehs, int vehIdx)
        {
            int allVehCount = (vehIdx == -1) ? vehs.Count : 1;
            this.EnsureCapacity(allVehCount);
            this._techIndex = techIdx;
            Manufacturer            mfr = this._modelYearData[this._yrIndex].Manufacturers[this._mfrIndex];
            Manufacturer.CModelData mmd = mfr.ModelData;
            int yrLen = this._endYear - ModelYear.MinYear + 1;
            int vIdxOff = (vehIdx == -1) ? 0 : vehIdx;
            for (int i = 0; i < allVehCount; i++)
            {
                Vehicle veh = vehs[i + vIdxOff];
                ModelYear lingerYear;
                bool      lingering;
                bool      isTechValid = TA.IsTechValid(this._modelYearData, this._year, veh, this._techGroup[techIdx].Index,
                                                       this._ignorePhaseIn, false, out lingering, out lingerYear);
                if (this._doInherit || isTechValid)
                {   
                    this._vehs          [this._vehCount] = veh;
                    this._ignore        [this._vehCount] = 0;
                    this._techKeys      [this._vehCount] = TA.TechKeys[techIdx];
                    this._techLingerKeys[this._vehCount] = (lingering) ? TA.TechKeys[techIdx] : 0;
                    this._lingerYear    [this._vehCount] = (lingering) ? lingerYear.Year      : 0;
                    this._lingering     [this._vehCount] = (lingering) ? true                 : false;
                    if (this._techImpacts[this._vehCount] == null)
                    {
                        this._techImpacts[this._vehCount] = new TechImpact(yrLen);
                    }
                    if (this._yearOfInitialApplication[this._vehCount] == null)
                    {
                        this._yearOfInitialApplication[this._vehCount] = new int[TI.TechnologyCount];
                    }
                    if (this._backfill)
                    {
                        this.ParseVehTechsLists_Backfill(techIdx, veh, mmd);
                    }
                    if (lingering)
                    {
                        if (this._minLingerYear == 0 || this._minLingerYear > lingerYear.Year)
                        {
                            this._minLingerYear = lingerYear.Year;
                        }
                    }
                    this._vehCount++;
                }
            } 
            if (this._vehCount > 0)
            {   
                RegulatoryClass veh0rc = this._vehs[0].RegClass;
                for (int i = 1; i < this._vehCount; i++)
                {   
                    if (this._vehs[i].RegClass != veh0rc) { this._isRcMix = true; break; }
                }
                if (this._vehCount != allVehCount) { this._isEnablesMix = true; }
                else
                {   
                    for (int i = 1; i < this._vehCount; i++)
                    {   
                        if (this._techKeys[i] != this._techKeys[0]) { this._isEnablesMix = true; break; }
                    }
                }
            } 
        }
        void ParseVehTechsLists_Backfill(int techIdx, Vehicle veh, Manufacturer.CModelData mmd)
        {
            TechnologyClass    techClass = veh.TechnologyClass;
            RegulatoryClass    regClass  = veh.RegClass;
            Vehicle.CModelData vmd       = veh.ModelData;
            for (int i = techIdx - 1; i >= 0; i--)
            {   
                int i2 = -1, iTmp = i;  
                switch (this._techGroup[i].Index)
                {   
                    case TI.DVVLD: i2 = this.FindTechInGroup(TI.CVVL); break;
                    case TI.CVVL:
                        continue;       
                }
                if (i2 != -1)
                {   
                    Industry yrData = this._modelYearData[this._yrIndex];
                    if (TA.GetCost(this._techGroup[i2], veh, yrData, this._settings, this._year, this._year.Year, this._startYear, this._endYear, this._techGroup, this._techKeys[this._vehCount]) <
                        TA.GetCost(this._techGroup[i ], veh, yrData, this._settings, this._year, this._year.Year, this._startYear, this._endYear, this._techGroup, this._techKeys[this._vehCount]))
                    {   
                        Interaction.Swap(ref i, ref i2);
                    }
                }
                for (int j = 0; j < 2; j++)
                {   
                    int bftIndex = this._techGroup[i].Index;    
                    ModelYear lingerYear;
                    bool      lingering;
                    bool      isTechValid = TA.IsTechValid(this._modelYearData, this._year, veh, bftIndex, this._ignorePhaseIn,
                                                           true, out lingering, out lingerYear);
                    bool      canBackfill = TA.CanBackfill(i, this._techGroup, this._techKeys[this._vehCount], vmd.TechUsed);
                    if (isTechValid && canBackfill)
                    {
                        this._techKeys[this._vehCount] += TA.TechKeys[i];
                        if (lingering || this._lingering[this._vehCount])
                        {
                            this._techLingerKeys[this._vehCount] += TA.TechKeys[i];
                            if (lingering)
                            {
                                this._lingerYear[this._vehCount] = lingerYear.Year;
                                this._lingering [this._vehCount] = true;
                            }
                        }
                    }
                    if (i2 == -1) { break; }    
                    i = i2;                     
                }
                i = iTmp;
            } 
        }
        void SplitShared(bool splitMixedEnables, ref int cfCount)
        {
            double          eff   = double.NaN;
            RegulatoryClass effRC = RegulatoryClass.None;
            foreach (RegulatoryClass regClass in RCDouble.Classes)
            {
                bool hasVehs = this.SplitSharedHelper_EvalRegClass(regClass, splitMixedEnables, ref cfCount);
                if (!hasVehs) { continue; }
                if (double.IsNaN(eff) || this._efficiency < eff)
                {
                    eff   = this._efficiency;
                    effRC = regClass;
                }
            }
            if (effRC != RegulatoryClass.None)
            {   
                this.SplitSharedHelper_EvalRegClass(effRC, splitMixedEnables, ref cfCount);
                this._index = cfCount++;
                this._isSplitCf = true;
            }
        }
        bool SplitSharedHelper_EvalRegClass(RegulatoryClass regClass, bool splitMixedEnables, ref int cfCount)
        {
            int firstRcVeh = -1;    
            int rcVehCount =  0;    
            for (int j = 0; j < this._vehCount; j++)
            {
                if (this._vehs[j].RegClass == regClass)
                {   
                    this._ignore[j] = 0;                        
                    if (firstRcVeh == -1) { firstRcVeh = j; }   
                    rcVehCount++;                               
                }
                else { this._ignore[j] = 1; }
            }
            if (rcVehCount == 0) { return false; }
            bool isEnablesMix = false;
            if (splitMixedEnables)
            {
                for (int j = firstRcVeh + 1; j < this._vehCount; j++)
                {   
                    if (this._ignore[j] == 0 && this._techKeys[j] != this._techKeys[firstRcVeh])
                    {   
                        isEnablesMix = true; break;
                    }
                }
            }
            this._isEnablesMix = isEnablesMix;
            if (isEnablesMix) { this.SplitMixedEnables(true, ref cfCount); }
            else              { this.CalcEfficiency(); }
            return true;
        }
        void SplitMixedEnables(bool sortLists, ref int cfCount)
        {
            if (sortLists)
            {   
                for (int i = this._vehCount - 1; i >= 0; i--)
                {
                    for (int j = 1; j <= i; j++)
                    {
                        if (this._techKeys[j - 1] > this._techKeys[j])
                        {   
                            this.Swap(this._vehs          , j - 1, j);
                            this.Swap(this._ignore        , j - 1, j);
                            this.Swap(this._techKeys      , j - 1, j);
                            this.Swap(this._techLingerKeys, j - 1, j);
                        }
                    } 
                } 
            }
            double eff    = double.NaN;     
            ulong  effKey = 0;              
            ulong  key    = 0;              
            for (int i = 0; i < this._vehCount; i++)
            {
                if ((this._ignore[i] & 1) != 1)
                {   
                    if (key == this._techKeys[i]) { this._ignore[i] -= 2; }
                    else
                    {   
                        if (key != 0)
                        {   
                            this.CalcEfficiency();
                            if (double.IsNaN(eff) || this._efficiency < eff)
                            {   
                                eff    = this._efficiency;
                                effKey = key;
                            }
                        }
                        key = this._techKeys[i];
                        for (int j = 0; j < this._vehCount; j++) { this._ignore[j] |= 2; }
                        this._ignore[i] -= 2;   
                    }
                } 
            } 
            this.CalcEfficiency();
            if (double.IsNaN(eff) || this._efficiency < eff)
            {   
                eff    = this._efficiency;
                effKey = key;
            }
            if (effKey != 0)
            {
                for (int i = 0; i < this._vehCount; i++)
                {   
                    if (this._techKeys[i] == effKey) { this._ignore[i] &= 1; }
                    else                             { this._ignore[i] |= 2; }
                }
                this.CalcEfficiency();
                this._index     = cfCount++;
                this._isSplitCf = true;
            }
        }
        int FindTechInGroup(int techIdx)
        {
            for (int i = 0; i < this._techCount; i++)
            {
                if (this._techGroup[i].Index == techIdx) { return i; }
            }
            return -1;
        }
        void Swap(Vehicle[] arr, int i, int j) { Vehicle tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }
        void Swap(ulong  [] arr, int i, int j) { ulong   tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }
        void Swap(int    [] arr, int i, int j) { int     tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }
        #endregion
        #region 
        void CalcEfficiency()
        {
            this._efficiency = double.NaN;
            bool hasVehs = false;   
            for (int i = 0; i < this._vehCount; i++)
            {
                if (this._ignore[i] == 0 && this._vehs[i].RegClass != RegulatoryClass.None)
                {   
                    hasVehs = true; break;
                }
            }
            if (!hasVehs) { return; }   
            Industry     data = this._modelYearData[this._yrIndex];
            Manufacturer mfr  = data.Manufacturers[this._mfrIndex];
            bool mfrInCompliance = true;
            for (int i = 0; i < this._vehCount; i++)
            {
                if (this._ignore[i] == 0)
                {   
                    RegulatoryClass regClass = this._vehs[i].RegClass;
                    if (mfr.ModelData.Fines[regClass] > 0) { mfrInCompliance = false; break; }
                }
            }
            if (mfrInCompliance && !this._overcomply && !this._doInherit) { return; }
            this.CalcTechImpacts(data.ModelData);
            this._newCafe  = Standards.TestCAFE (mfr, this._scen, this._year, this._settings,                this._techImpacts, this._vehCount, false);
            this._newFines = Standards.TestFines(mfr, this._scen, this._year, this._settings, this._newCafe, this._techImpacts, this._vehCount);
            RCDouble effectiveNewCafe = Standards.TestCAFE (mfr, this._scen, this._year, this._settings,                                    this._techImpacts, this._vehCount, true);
            this._effectiveNewFines   = Standards.TestFines(mfr, this._scen, this._year, this._settings, effectiveNewCafe.CreateRounded(1), this._techImpacts, this._vehCount);
            this._curCafe  = Standards.TestCAFE (mfr, this._scen, this._year, this._settings,                null, 0, false);
            this._curFines = Standards.TestFines(mfr, this._scen, this._year, this._settings, this._curCafe, null, 0);
            RCDouble deltaFines = this._curFines - this._newFines;
            double totalDeltaFines = deltaFines.Total;
            if (totalDeltaFines <= 0 && !this._overcomply && !this._creditTrade && !this._doInherit)
            {
                this.IgnoreTechnologies();
                return;
            }
            Manufacturer.CModelData mmd = mfr.ModelData;
            double techCost      = 0;
            double affectedSales = 0;
            double fcSavings     = 0;
            double deltaRpe      = 0;
            double lossOfValue   = 0;
            double deltaCredits  = 0;
            bool[] creditsSaved  = new bool[ModelYear.ToIndex(this._endYear) + 1];
            for (int i = 0; i < this._vehCount; i++)
            {
                TechImpact impact = this._techImpacts[i];
                if (!impact.Ignore)
                {
                    for (int yrIndex = 0; yrIndex <= this._yrIndex; yrIndex++)
                    {
                        if (impact.HasImpact[yrIndex])
                        {
                            Vehicle         veh      = impact.Vehicle[yrIndex];
                            RegulatoryClass regClass = veh.RegClass;
                            VehicleClass    vehClass = veh.VehicleClass;
                            VehicleStyle    vehStyle = veh.VehicleStyle;
                            FuelValue       vehFE    = veh.Description.FuelEconomy;
                            FuelValue       vehShare = veh.Description.FuelShare;
                            FuelValue       newFE    = impact.NewFuelEconomy[yrIndex];
                            FuelValue       newShare = impact.NewFuelShare  [yrIndex];
                            double          sales    = veh.Description.Sales[yrIndex];
                            ModelYear       lingerMY = ModelYear.NewModelYearFromIndex(yrIndex);
                            KMYType   kmy = (mfrInCompliance) ? mmd.KMY_OC[yrIndex] : mmd.KMY[yrIndex];
                            FuelValue gap = this._parameters.EconomicValues.OnRoadGap[veh.VehicleClass];
                            double vehFuelCost = Standards.GetVehicleFuelCost(vehFE, vehShare, regClass, this._scen, lingerMY, gap, kmy[vehClass, vehStyle]);
                            double newFuelCost = Standards.GetVehicleFuelCost(newFE, newShare, regClass, this._scen, lingerMY, gap, kmy[vehClass, vehStyle]);
                            techCost      += sales * impact.Cost[yrIndex];
                            affectedSales += sales;
                            fcSavings     += sales * (vehFuelCost - newFuelCost);
                            lossOfValue   += sales * impact.LossOfValue[yrIndex];
                            if (!this._regClasses.Contains(regClass))
                            {
                                this._regClasses.Add(regClass);
                            }
                        } 
                    } 
                } 
            } 
            if (fcSavings <= 0 && (totalDeltaFines + deltaCredits) <= 0 && !this._doInherit)
            {
                this.IgnoreTechnologies();
                return;
            }
            deltaRpe = techCost; 
            this._efficiency = (deltaRpe - totalDeltaFines - fcSavings - deltaCredits + lossOfValue) / affectedSales;
            this._costPerMPG = (techCost - totalDeltaFines             - deltaCredits              ) / affectedSales;
            this._techCost           = techCost;
            this._totalDeltaRPE      = deltaRpe;
            this._totalDeltaFines    = totalDeltaFines;
            this._totalDeltaCredits  = deltaCredits;
            this._totalFuelSavings   = fcSavings;
            this._totalAffectedSales = affectedSales;
        }
        Vehicle CalcTechImpacts_GetVehicle(int vehIndex, int yrIndex)
        {
            Vehicle veh = this._vehs[vehIndex];
            if (yrIndex == this._yrIndex)
            {   
                return veh;
            }
            else
            {   
                Manufacturer  lMfr  = this._modelYearData[yrIndex].Manufacturers[this._mfrIndex];
                List<Vehicle> lVehs = lMfr.Vehicles;
                int     lCode = veh.Description.Code;
                Vehicle lVeh  = null;
                for (int i = 0; i < lVehs.Count; i++)
                {
                    if (lVehs[i].Description.Code == lCode) { lVeh = lVehs[i]; break;  }
                }
                return lVeh;
            } 
        }
        void CalcTechImpacts(Industry.CModelData imd)
        {
            for (int i = 0; i < this._vehCount; i++)
            {   
                this._techImpacts[i].Reset();
                if (this._ignore[i] != 0) { this._techImpacts[i].Ignore = true; continue; }
                Vehicle curVeh        = this._vehs          [i];                            
                ulong   curTechKey    = this._techKeys      [i];                            
                ulong   lingerTechKey = this._techLingerKeys[i];                            
                bool    lingering     = this._lingering     [i];                            
                int     lingerYrYear  = (lingering) ? this._lingerYear[i] : this._yrYear;   
                int     lingerYrIndex = lingerYrYear - ModelYear.MinYear;                   
                TechImpact impact = this._techImpacts[i];
                Array.Clear(this._yearOfInitialApplication[i], 0, TI.TechnologyCount);
                for (int j = lingerYrIndex; j <= this._yrIndex; j++)
                {   
                    Vehicle veh = this.CalcTechImpacts_GetVehicle(i, j);
                    if (veh == null) { impact.Ignore = true; break; }
                    ModelYear            year      = ModelYear.NewModelYearFromIndex(j);    
                    Industry             data      = this._modelYearData[j];
                    Vehicle.CDescription vd        = veh.Description;
                    Vehicle.CModelData   vmd       = veh.ModelData;
                    TechnologyClass      techClass = curVeh.TechnologyClass;
                    RegulatoryClass      regClass  = curVeh.RegClass;
                    ulong                techKey   = (j == this._yrIndex) ? curTechKey : lingerTechKey;
                    HEVType  vehHevType  = veh.HEVType;
                    FuelType vehFuelType = vd .FuelShare.FuelType;
                    if (techKey != 0)
                    {   
                        impact.HasImpact[j] = true;
                        impact.Vehicle  [j] = veh;      
                        impact.NewFuelShare   [j] = vd.FuelShare;
                        impact.NewFuelEconomy [j] = vd.FuelEconomy;
                        impact.NewFuelEconomy2[j] = vd.FuelEconomy2;
                        Array.Clear(this._committedERPCosts, 0, TI.TechnologyCount);
                        for (int k = 0; k < this._techCount; k++)
                        {
                            if ((techKey & TA.TechKeys[k]) != 0)
                            {   
                                Technology tech = this._techGroup[k];
                                impact.LossOfValue[j] += tech.Attributes[veh].LossOfValue;
                                if (this._yearOfInitialApplication[i][tech.Index] == 0) { this._yearOfInitialApplication[i][tech.Index] = j; }
                                double erpCost;
                                double cost = TA.GetCost(tech, veh, data, this._settings, year, this._yearOfInitialApplication[i][tech.Index],
                                    this._startYear, this._endYear, this._techGroup, techKey, out erpCost, ref this._committedERPCosts);
                                impact.Cost           [j] += cost;
                                impact.ERPCost        [j] += erpCost;
                                impact.MaintenanceCost[j] += TA.GetMaintenanceCost(tech, veh, year);
                                impact.RepairCost     [j] += TA.GetRepairCost     (tech, veh, year);
                                double fc, fc0FS, synergy, fcNoSyn;
                                fc = TA.GetImprovement(tech, veh, this._scen, year, this._techGroup, techKey, out synergy, out fc0FS);
                                fcNoSyn = fc - synergy;
                                impact.OffCycleCredit[j] += tech.OffCycleCredit[veh.RegClass];
                                FuelType primaryFuel = impact.NewFuelShare[j].PrimaryFuel;
                                if (TI.IsConversionToDiesel(tech.Index) && (vehFuelType & FuelType.Diesel) != FuelType.Diesel)
                                {
                                    this.CalcTechImpacts_ConvertFuel(impact, j, primaryFuel, FuelType.Diesel, fc, fcNoSyn);
                                    vehFuelType = FuelType.Diesel;  
                                }
                                else if (TI.IsConversionToCNG(tech.Index) && (vehFuelType & FuelType.CNG) != FuelType.CNG)
                                {
                                    this.CalcTechImpacts_ConvertFuel(impact, j, primaryFuel, FuelType.CNG, fc, fcNoSyn);
                                    vehFuelType = FuelType.CNG;     
                                }
                                else if (TI.IsConversionToLNG(tech.Index) && (vehFuelType & FuelType.LNG) != FuelType.LNG)
                                {
                                    this.CalcTechImpacts_ConvertFuel(impact, j, primaryFuel, FuelType.LNG, fc, fcNoSyn);
                                    vehFuelType = FuelType.LNG;     
                                }
                                else if (TI.IsConversionToLPG(tech.Index) && (vehFuelType & FuelType.LPG) != FuelType.LPG)
                                {
                                    this.CalcTechImpacts_ConvertFuel(impact, j, primaryFuel, FuelType.LPG, fc, fcNoSyn);
                                    vehFuelType = FuelType.LPG;     
                                }
                                else if (TI.IsConversionToSHEV(tech.Index) && vehHevType != HEVType.StrongHybrid)
                                {   
                                    impact.NewFuelEconomy[j] = impact.NewFuelEconomy2[j];
                                    this.CalcTechImpacts_ConvertFuel(impact, j, primaryFuel, primaryFuel, fc, fcNoSyn);
                                    vehHevType = HEVType.StrongHybrid;
                                }
                                else if (TI.IsConversionToPHEV(tech.Index))
                                {
                                    if (vehHevType != HEVType.PlugInHybrid)
                                    {   
                                        double primaryFE  = impact.NewFuelEconomy [j][primaryFuel] / (1 - synergy);
                                        double primaryFE2 = impact.NewFuelEconomy2[j][primaryFuel];
                                        this.CalcTechImpacts_ConvertFuel(impact, j, primaryFuel, FuelType.Electricity, fc, fcNoSyn);
                                        impact.NewFuelEconomy [j][primaryFuel         ] =      primaryFE;
                                        impact.NewFuelEconomy2[j][primaryFuel         ] =      primaryFE2;
                                        impact.NewFuelShare   [j][primaryFuel         ] =      fc0FS;
                                        impact.NewFuelShare   [j][FuelType.Electricity] = (1 - fc0FS);
                                        vehHevType = HEVType.PlugInHybrid;
                                    }
                                    else
                                    {   
                                        impact.NewFuelEconomy [j][primaryFuel         ] /= (1 - synergy);
                                        impact.NewFuelShare   [j][primaryFuel         ]  =      fc0FS;
                                        impact.NewFuelEconomy [j][FuelType.Electricity] /= (1 - fc);
                                        impact.NewFuelEconomy2[j][FuelType.Electricity] /= (1 - fcNoSyn);
                                        impact.NewFuelShare   [j][FuelType.Electricity]  = (1 - fc0FS);
                                    }
                                }
                                else if (TI.IsConversionToEV(tech.Index))
                                {   
                                    impact.NewFuelEconomy [j][FuelType.Electricity] /= (1 - fc);
                                    impact.NewFuelEconomy2[j][FuelType.Electricity] /= (1 - fcNoSyn);
                                    impact.NewFuelEconomy [j][primaryFuel         ]  =  0;
                                    impact.NewFuelEconomy2[j][primaryFuel         ]  =  0;
                                    impact.NewFuelShare   [j][FuelType.Electricity]  =  1;
                                    impact.NewFuelShare   [j][primaryFuel         ]  =  0;
                                    vehHevType = HEVType.PureElectric;
                                }
                                else if (TI.IsConversionToFCV(tech.Index) && vehHevType != HEVType.FuelCell)
                                {
                                    this.CalcTechImpacts_ConvertFuel(impact, j, FuelType.Electricity, FuelType.Hydrogen, fc, fcNoSyn);
                                    vehHevType = HEVType.FuelCell;  
                                }
                                else
                                {   
                                    if (impact.NewFuelShare[j].IsMultiFuel)
                                    {
                                        foreach (FuelType f in FTValue<object>.Classes)
                                        {
                                            if (impact.NewFuelShare[j][f] > 0)
                                            {
                                                impact.NewFuelEconomy [j][f] /= (1 - fc);
                                                impact.NewFuelEconomy2[j][f] /= (1 - fcNoSyn);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        impact.NewFuelEconomy [j][primaryFuel] /= (1 - fc);
                                        impact.NewFuelEconomy2[j][primaryFuel] /= (1 - fcNoSyn);
                                    }
                                }
                                if (TI.IsMaterialSubstitution(tech.Index) ||
                                    TI.IsConversionToSHEV    (tech.Index) ||
                                    TI.IsConversionToPHEV    (tech.Index) ||
                                    TI.IsConversionToEV      (tech.Index))
                                {
                                    double newCW, newGVWR, newGCWR;
                                    TechnologyApplicability.GetWeightChange(tech, veh, this._scen, year, out newCW, out newGVWR, out newGCWR);
                                    impact.DeltaCurbWeight[j] = newCW   - vd.CurbWeight;
                                    impact.DeltaGVWR      [j] = newGVWR - vd.GVWR;
                                    impact.DeltaGCWR      [j] = newGCWR - vd.GCWR;
                                }
                            } 
                        } 
                    } 
                } 
                if (!impact.Ignore)
                {   
                    for (int j = lingerYrIndex; j <= this._yrIndex; j++)
                    {
                        if (!impact.HasImpact[j]) { continue; }
                        RegulatoryClass vehRegClass    = impact.Vehicle[j].RegClass;
                        FuelValue       vehFS          = impact.Vehicle[j].Description.FuelShare;
                        FuelValue       vehFE1         = impact.Vehicle[j].Description.FuelEconomy;
                        FuelValue       vehFE2         = impact.Vehicle[j].Description.FuelEconomy2;
                        ModelYear       year           = ModelYear.NewModelYearFromIndex(j);    
                        double          vehFE1Combined = Standards.GetAverageFuelEconomy(vehFE1, vehFS, vehRegClass, this._scen, year, this._settings);
                        double          vehFE2Combined = Standards.GetAverageFuelEconomy(vehFE2, vehFS, vehRegClass, this._scen, year, this._settings);
                        foreach (FuelType f in FTValue<object>.Classes)
                        {   
                            impact.Improvement [j][f] = (impact.NewFuelEconomy [j][f] == 0) ? 0 : ((vehFE1[f] == 0) ? vehFE1Combined : vehFE1[f]) / impact.NewFuelEconomy [j][f];
                            impact.Improvement2[j][f] = (impact.NewFuelEconomy2[j][f] == 0) ? 0 : ((vehFE2[f] == 0) ? vehFE2Combined : vehFE2[f]) / impact.NewFuelEconomy2[j][f];
                            impact.DeltaFuelEconomy [j][f] = impact.NewFuelEconomy [j][f] - vehFE1[f];
                            impact.DeltaFuelEconomy2[j][f] = impact.NewFuelEconomy2[j][f] - vehFE2[f];
                        }
                    } 
                } 
            } 
        }
        void CalcTechImpacts_ConvertFuel(TechImpact impact, int yr, FuelType fromFuel, FuelType toFuel, double fc, double fcNoSyn)
        {
            double newFEValue  = impact.NewFuelEconomy [yr][fromFuel] / (1 - fc);
            double newFE2Value = impact.NewFuelEconomy2[yr][fromFuel] / (1 - fcNoSyn);
            impact.NewFuelEconomy [yr].Clear();
            impact.NewFuelEconomy2[yr].Clear();
            impact.NewFuelShare   [yr].Clear();
            impact.NewFuelEconomy [yr][toFuel] = newFEValue;
            impact.NewFuelEconomy2[yr][toFuel] = newFE2Value;
            impact.NewFuelShare   [yr][toFuel] = 1;
        }
        public void IgnoreTechnologies()
        {
            for (int i = 0; i < this._vehCount; i++)
            {
                if (this._ignore[i] == 0)
                {   
                    Vehicle.CModelData vmd = this._vehs[i].ModelData;
                    for (int j = 0; j < this._techCount; j++)
                    {   
                        if ((this._techKeys[i] & TA.TechKeys[j]) != 0)
                        {
                            vmd.TechIgnored[this._techGroup[j].Index] = true;
                        }
                    } 
                } 
            } 
        }
        #endregion
        #region 
        public void ApplyFinding()
        {
            if (!this.IsValid  ) { throw new InvalidOperationException("The ComplianceFinding being applied is not valid."); }
            if (this._isApplied) { throw new InvalidOperationException("The ComplianceFinding has already been applied."  ); }
            this._isApplied = true;
            this.LogFinding();
            this.ApplyFinding_CheckSplitCf();
            for (int i = 0; i < this._vehCount; i++)
            {
                if (this._ignore[i] == 0)
                {   
                    this.ApplyTechImpact(this._techImpacts[i], this._techKeys[i], this._techLingerKeys[i], this._lingerYear[i],
                        this._yearOfInitialApplication[i]);
                }
            }
        }
        void ApplyTechImpact(TechImpact impact, ulong curTechKey, ulong lingerTechKey, int lingerYear, int[] yearOfInitApplication)
        {
            for (int i = 0; i <= this._yrIndex; i++)
            {
                ModelYear year = new ModelYear(ModelYear.MinYear + i);
                if (impact.HasImpact[i])
                {   
                    Vehicle                   veh = impact.Vehicle[i];
                    Vehicle     .CDescription vd  = veh.Description;
                    Vehicle     .CModelData   vmd = veh.ModelData;
                    Manufacturer.CModelData   mmd = veh.Manufacturer.ModelData;
                    ulong techKey = (i == this._yrIndex) ? curTechKey : lingerTechKey;
                    RegulatoryClass regClass = veh.RegClass;
                    VehicleClass    vehClass = veh.VehicleClass;
                    double          vehSales = vd.Sales[i];
                    ulong baseTech = 0;
                    for (int j = this._techCount - 1; j >= 0; j--)
                    {   
                        if ((techKey & TA.TechKeys[j]) != 0) { baseTech = TA.TechKeys[j]; break; }
                    }
                    for (int j = 0; j < this._techCount; j++)
                    {   
                        if ((techKey & TA.TechKeys[j]) != 0)
                        {   
                            int techIndex = this._techGroup[j].Index;
                            this.ApplyTechnology(veh, vmd, mmd, regClass, vehSales, this._techGroup[j], year, lingerYear,
                                yearOfInitApplication[techIndex], TA.TechKeys[j] != baseTech);
                        }
                    } 
                    if (impact.HasWeightChanged(i)) { vd.CurbWeight += impact.DeltaCurbWeight[i]; }
                    if (impact.HasGVWRChanged  (i)) { vd.GVWR       += impact.DeltaGVWR      [i]; }
                    if (impact.HasGCWRChanged  (i)) { vd.GCWR       += impact.DeltaGCWR      [i]; }
                    if (!impact.NewFuelShare[i].IsValid || !impact.NewFuelEconomy[i].IsValid || !impact.NewFuelEconomy2[i].IsValid)
                    {
                        throw new InvalidOperationException(
                            "Error in ComplianceFinding.  Vehicle's new Fuel Economy and/or Fuel Share values are not valid.");
                    }
                    else
                    {
                        vd.FuelShare    = impact.NewFuelShare   [i];
                        vd.FuelEconomy  = impact.NewFuelEconomy [i];
                        vd.FuelEconomy2 = impact.NewFuelEconomy2[i];
                    }
                    vmd.OffCycleCredit            +=             impact.OffCycleCredit [i];
                    vmd.TechCost                  +=             impact.Cost           [i] ;
                    vmd.LossOfValue               +=             impact.LossOfValue    [i] ;
                    vmd.MaintenanceCost           +=             impact.MaintenanceCost[i] ;
                    vmd.RepairCost                +=             impact.RepairCost     [i] ;
                    mmd.TechCost       [regClass] += (vehSales * impact.Cost           [i]);
                    mmd.LossOfValue    [regClass] += (vehSales * impact.LossOfValue    [i]);
                    mmd.MaintenanceCost[regClass] += (vehSales * impact.MaintenanceCost[i]);
                    mmd.RepairCost     [regClass] += (vehSales * impact.RepairCost     [i]);
                } 
            } 
        }
        void ApplyTechnology(Vehicle veh, Vehicle.CModelData vmd, Manufacturer.CModelData mmd, RegulatoryClass regClass,
            double vehSales, Technology tech, ModelYear year, int lingerYear, int yearOfInitApplication, bool backfilling)
        {
            bool isInherited = (this._doInherit || lingerYear != this._minLingerYear);
            TA.InstallTechnologyOnVehicle(veh, tech, year, yearOfInitApplication, isInherited, this._settings);
            mmd.TechUsedSales   [tech.Index][regClass] += vehSales;
            mmd.TechAppliedSales[tech.Index][regClass] += vehSales;
            if (!this._settings.OperatingModes.IgnorePhaseIn)
            {   
                TA.CheckPhaseIn(this._settings, mmd, tech, year, this._startYear - ModelYear.MinYear);
            }
        }
        void ApplyFinding_CheckSplitCf()
        {
            if (!this._isSplitCf || this._techInheriting) { return; }
            List<Vehicle>[] splitVehs = new List<Vehicle>[this._yrIndex + 1];
            int initialYear = 9999; 
            for (int i = 0; i < this._vehCount; i++)
            {
                TechImpact impact = this._techImpacts[i];
                if (impact.Ignore) { continue; }
                for (int j = 0; j <= this._yrIndex; j++)
                {
                    if (impact.HasImpact[j])
                    {   
                        if (splitVehs[j] == null) { splitVehs[j] = new List<Vehicle>(); }
                        splitVehs[j].Add(impact.Vehicle[j]);
                        initialYear = Math.Min(initialYear, ModelYear.MinYear + j);
                    }
                } 
            } 
            int code = -1;
            for (int i = this._yrIndex; i >= 0; i--)
            {
                if (splitVehs[i] != null)
                {   
                    if (this._techType == TechnologyType.Engine || this._techType == TechnologyType.DieselEngine)
                    {   
                        Engine eng = splitVehs[i][0].Engine.Split(splitVehs[i], code);
                        splitVehs[i][0].Manufacturer.Engines.Add(eng);
                        code = eng.Description.Code;
                    }
                    else if (this._techType == TechnologyType.Transmission)
                    {   
                        Transmission trn = splitVehs[i][0].Transmission.Split(splitVehs[i], code);
                        splitVehs[i][0].Manufacturer.Transmissions.Add(trn);
                        code = trn.Description.Code;
                    }
                    else if (this._techType == TechnologyType.MR || this._techType == TechnologyType.Aerodynamics)
                    {   
                        Platform plt = splitVehs[i][0].Platform.Split(splitVehs[i], initialYear);
                        splitVehs[i][0].Manufacturer.Platforms.Add(plt);
                    }
                    else
                    {
                        Console.WriteLine(this._techType + " -- attempted to split vehicle application ... this is an ERROR!!");
                    }
                } 
            } 
        }
        #endregion
        #region 
        public void LogFinding()
        {
            if (!this.IsValid || this._logWriter.IsEmpty) { return; }
            Manufacturer mfr = this._modelYearData[this._yrIndex].Manufacturers[this._mfrIndex];
            ComplianceWriter log    = (this._logWriter.CFs    == null) ? null : this._logWriter.CFs   [this._scen.Index];
            ComplianceWriter extLog = (this._logWriter.ExtCFs == null) ? null : this._logWriter.ExtCFs[this._scen.Index];
            if (log    != null && this._isApplied) { this.LogFinding(log   , mfr.ModelData); }
            if (extLog != null                   ) { this.LogFinding(extLog, mfr.ModelData); }
        }
        void LogFinding(ComplianceWriter cw, Manufacturer.CModelData mmd)
        {
            StringBuilder line = new StringBuilder(1024);
            int lineNumber = cw.LineCount + 1;
            for (int i = 0; i < this._vehCount; i++)
            {
                TechImpact impact = this._techImpacts[i];
                if (!impact.Ignore)
                {   
                    Vehicle baseVeh     = impact.Vehicle[this._yrIndex];
                    int     baseVehCode = baseVeh.Description.Code;
                    for (int j = impact.HasImpact.Length - 1; j >= 0; j--)
                    {   
                        if (!impact.HasImpact[j]) { continue; }
                        Vehicle              veh = impact.Vehicle[j];
                        Vehicle.CDescription vd  = veh.Description;
                        ulong techKey = (j == this._yrIndex) ? this._techKeys[i] : this._techLingerKeys[i];
                        line.Append(lineNumber++);                                line.Append(TAB);
                        line.Append((this._doInherit) ? "I" :
                                                        this._index.ToString());  line.Append(TAB);
                        line.Append(this._scen.Index);                            line.Append(TAB);
                        line.Append(vd.Manufacturer);                             line.Append(TAB);
                        line.Append((!this._isApplied  ) ? '\0' :
                                    ( this._doInherit  ) ?  'I' :
                                    ( this._overcomply ) ?  'O' :
                                                            'Y');                 line.Append(TAB);
                        line.Append(this._efficiency);                            line.Append(TAB);
                        line.Append(this._costPerMPG);                            line.Append(TAB);
                        line.Append(this._totalDeltaRPE     .ToString("0.####")); line.Append(TAB);
                        line.Append(this._totalDeltaFines   .ToString("0.####")); line.Append(TAB);
                        line.Append(this._totalDeltaCredits .ToString("0.####")); line.Append(TAB);
                        line.Append(this._totalFuelSavings  .ToString("0.####")); line.Append(TAB);
                        line.Append(this._totalAffectedSales.ToString("0.####")); line.Append(TAB);
                        RegulatoryClass regClass = veh.RegClass;
                        line.Append(               regClass ); line.Append(TAB);
                        line.Append(mmd.Standard  [regClass]); line.Append(TAB);
                        line.Append(this._curCafe [regClass]); line.Append(TAB);
                        line.Append(this._newCafe [regClass]); line.Append(TAB);
                        line.Append(this._curFines[regClass]); line.Append(TAB);
                        line.Append(this._newFines[regClass]); line.Append(TAB);
                        if (baseVehCode != vd.Code)
                        {   
                            line.Append('['); line.Append(vd.Code); line.Append(']');
                        }
                        line.Append(vd .Code            ); line.Append(TAB);
                        line.Append(vd .EngineCode      ); line.Append(TAB);
                        line.Append(vd .TransmissionCode); line.Append(TAB);
                        line.Append(veh.TechnologyClass ); line.Append(TAB);
                        bool isFirstTech = true;
                        for (int k = 0; k < this._techCount; k++)
                        {
                            if ((techKey & TA.TechKeys[k]) != 0)
                            {   
                                if (isFirstTech) { isFirstTech = false; } 
                                else { line.Append(", "); }
                                line.Append(this._techGroup[k].Abbr);
                            } 
                        } 
                        line.Append(TAB);
                        line.Append(j + ModelYear.MinYear);                          line.Append(TAB);
                        line.Append(vd    .FuelEconomy         .ToString("0.####")); line.Append(TAB);
                        line.Append(vd    .FuelShare           .ToString("0.####")); line.Append(TAB);
                        line.Append(impact.NewFuelEconomy   [j].ToString("0.####")); line.Append(TAB);
                        line.Append(impact.NewFuelShare     [j].ToString("0.####")); line.Append(TAB);
                        line.Append(vd    .Sales            [j].ToString(        )); line.Append(TAB);
                        line.Append(impact.Cost             [j].ToString("0.####")); line.Append(TAB);
                        line.Append(impact.ERPCost          [j].ToString("0.####")); line.Append(TAB);
                        line.Append(impact.Improvement      [j].ToString("0.####")); line.Append(TAB);
                        line.Append(impact.DeltaFuelEconomy [j].ToString("0.####")); line.Append(TAB);
                        line.Append(impact.OffCycleCredit   [j].ToString("0.####")); line.Append(TAB);
                        line.Append(impact.DeltaCurbWeight  [j].ToString("0.####")); line.Append(TAB);
                        line.Append(impact.DeltaGVWR        [j].ToString("0.####")); line.Append(TAB);
                        line.Append(impact.DeltaGCWR        [j].ToString("0.####")); line.Append(TAB);
                        line.Append(impact.NewFuelEconomy2  [j].ToString("0.####")); line.Append(TAB);
                        line.Append(impact.Improvement2     [j].ToString("0.####")); line.Append(TAB);
                        line.Append(impact.DeltaFuelEconomy2[j].ToString("0.####"));
                        line.Append(NewLine);
                    } 
                } 
            }
            cw.Write(line.ToString());
        }
        #endregion
        #endregion
        #region 
        public int Index { get { return this._index; } }
        public bool IsValid { get { return !double.IsNaN(this._efficiency); } }
        public bool IsEfficient { get { return (this._efficiency < 0); } }
        public bool InCompliance { get { return this.IsValid && (this._effectiveNewFines.Total <= 0); } }
        public bool IsApplied { get { return this._isApplied; } }
        public double Efficiency { get { return this._efficiency; } }
        public double CostPerMPG { get { return this._costPerMPG; } }
        public double TechCost { get { return this._techCost; } }
        public List<RegulatoryClass> RegClasses { get { return this._regClasses; } }
        #endregion
        #region 
        const string TAB     = "\t";
        const string TAB_8   = "\t\t\t\t\t\t\t\t";
        const string TAB_14  = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
        const string NewLine = "\n";
        Scenario             _scen;
        ModelYear            _year;
        int                  _yrIndex;          
        int                  _yrYear;           
        int                  _startYear;
        int                  _endYear;
        Industry[]           _modelYearData;
        int                  _mfrIndex;
        ModelingSettings     _settings;
        Parameters           _parameters;       
        bool                 _multiYear;
        bool                 _ignorePhaseIn;
        bool                 _techInheriting;
        bool                 _backfill;
        LogWriter            _logWriter;
        TechnologyType       _techType;         
        List<Technology>     _techGroup;        
        int                  _techCount;
        bool                 _doInherit;        
        internal int _index;                    
        internal int _vehCount;                 
        internal int _capacity;                 
        internal Vehicle   [] _vehs;            
        internal int       [] _ignore;          
        int                   _techIndex;       
        internal ulong     [] _techKeys;        
        internal ulong     [] _techLingerKeys;  
        int                [] _lingerYear;      
        int                   _minLingerYear;   
        bool               [] _lingering;       
        internal TechImpact[] _techImpacts;     
        int              [][] _yearOfInitialApplication;
        bool[] _committedERPCosts;
        internal bool _hasVehs;
        internal bool _isRcMix;                 
        internal bool _isEnablesMix;            
        internal bool _isSplitCf;
        internal bool _isApplied;
        internal bool _overcomply;
        internal bool _creditTrade;
        internal double _efficiency;
        internal double _techCost;
        internal RCDouble _newCafe;             
        RCDouble _newFines;                     
        RCDouble _effectiveNewFines;            
        RCDouble _curCafe;                      
        RCDouble _curFines;                     
        double _costPerMPG;
        double _totalDeltaRPE;
        double _totalDeltaFines;
        double _totalDeltaCredits;
        double _totalFuelSavings;
        double _totalAffectedSales;
        List<RegulatoryClass> _regClasses;      
        #endregion
    }
}

