/* 
   Copyright (C) 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 
   2022, 2023 German A. Arias

   This file is part of FísicaLab application

   FÃ­sicaLab is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public
   License as published by the Free Software Foundation; either
   version 3 of the License, or (at your option) any later version.
 
   This application is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.
 
   You should have received a copy of the GNU General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/

#import <stdlib.h>
#import <stdio.h>
#import <math.h>
#import <gsl/gsl_vector.h>
#import <gsl/gsl_multiroots.h>
#import <gsl/gsl_rng.h>
//#import "TablesManager.h"
#import "FLThermodynamics.h"

static int buildSystem (const gsl_vector *vrs, void *params, gsl_vector *func)
{
  // Get FL object.
  FLThermodynamics *FLObj =
    (__bridge FLThermodynamics *)(params);

  [FLObj buildSystemWithUnknowns: vrs
                    forFunctions: func];

  return GSL_SUCCESS;
}


@interface FLThermodynamics (Private)
- (void) makeSystem;
//- (void) selectTablesFor: (NSString *)substance;
@end

@implementation FLThermodynamics (Private)
- (void) makeSystem
{
  int increase = 1;
  double newValue;
  BOOL follow = NO;
  
  const gsl_multiroot_fsolver_type *T;
  gsl_multiroot_fsolver *s;
  
  int status = 0, statusInt, k;
  int nvar  = [unknowns count];
  const size_t n = nvar;
  double par;
  NSString *message;
  size_t iter;
  
  gsl_vector *x = gsl_vector_alloc (n);
  int countRes = 0;
  id anObj;
  NSMutableArray *results = [NSMutableArray array];
  NSEnumerator *varCount;

  // id dataToCheck;
  // NSNumber *typeObject;
  // NSMutableArray *checkData;
  // NSEnumerator *enumerator;
  
  // Generator of random numbers
  const gsl_rng_type * Y;
  gsl_rng * r;
  gsl_rng_env_setup();
  Y = gsl_rng_default;
  r = gsl_rng_alloc (Y);
  
  // Search a solution
  do
    {
      gsl_multiroot_function f = {&buildSystem, n, (__bridge void *)(self)};

      iter = 0;
      if (increase <= 30)
	{
	  newValue = 100;
	}
      else if (increase <= 60)
	{
	  newValue = 1000;
	}
      else if (increase <= 75)
	{
	  newValue = 10000;
	}
      else
	{
	  newValue = 100000;
	}
      
      for (k = 0; k < nvar; k++)
	{
	  par = newValue*(gsl_rng_uniform (r));
	  gsl_vector_set (x, k, par);
	}
      
      T = gsl_multiroot_fsolver_hybrids;
      s = gsl_multiroot_fsolver_alloc (T, nvar);
      gsl_multiroot_fsolver_set (s, &f, x);
      
      do
	{
	  iter++;
	  statusInt = gsl_multiroot_fsolver_iterate (s);
	  
	  if (statusInt)
	    break;
	  
	  status = gsl_multiroot_test_residual (s->f, 1e-7);
	}
      while (status == GSL_CONTINUE && iter < 1000);  
      
      // Verify the status
      if ( (statusInt) && (increase < 90) )
	{
	  increase += 1;
	  follow = YES;
	}
      else
	{
	  follow = NO;
	}
      // End of verification
    }
  while (follow);
  // End of solution search

  // Move the solution data to array results
  varCount = [unknowns objectEnumerator];
  
  while ((anObj = [varCount nextObject]))
    {
      [results addObject: [NSNumber numberWithDouble:
				      gsl_vector_get (s->x, countRes)]];
      countRes += 1;
    }

  // Write the solution
  [self printUnknowns: unknowns withResults: results];
  
  // Write the status
  message = [NSString stringWithFormat: _(@"Status = %s \n"),
		      gsl_strerror (status)];
  [self writeMessage: message];
  
  gsl_multiroot_fsolver_free (s);
  gsl_vector_free (x);
  gsl_rng_free (r);
}

// - (void) selectTablesFor: (NSString *)substance
// {
//   [[TablesManager sharedTablesManager] selectTablesFor: substance];
// }

@end

@implementation FLThermodynamics

+ (NSArray *) dataForElementWithTag: (NSUInteger)tag forSystem: (NSUInteger)sys
{
  NSString *gravity, *image = nil;
  NSArray *titleList = nil, *unitsList = nil;
  NSMutableArray *dataList = nil;

  if (sys == 0)
    {
      gravity = @"9.81";
    }
  else
    {
      gravity = @"32.2";
    }

  switch (tag)
    {
    case 1:
      {
	// Reference system
	titleList = [NSArray arrayWithObjects: @"g", @"t", nil];
	unitsList = [NSArray arrayWithObjects: @"L/T2", @"T", nil];
	dataList = [NSMutableArray arrayWithObjects: gravity, @"0", nil];
	image = @"clock";
      }
      break;
    case 2:
      {
	// Heat
	titleList = [NSArray arrayWithObjects: @"Q", nil];
	unitsList = [NSArray arrayWithObjects: @"M*L2/T2", nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", nil];
	image = @"heat_in";
      }
      break;
    case 3:
      {
	// Work
	titleList = [NSArray arrayWithObjects: @"W", nil];
	unitsList = [NSArray arrayWithObjects: @"M*L2/T2", nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", nil];
	image = @"work_machine";
      }
      break;
    case 4:
      {
	// Data at some state
	titleList = [NSArray arrayWithObjects: _(@"Name"), @"p", @"T", @"u",
			     @"h", @"d", @"V", @"y", nil];
	unitsList = [NSArray arrayWithObjects: @"nil", @"M/(T2*L)", @"O",
			     @"(M*L2/T2)/M", @"(M*L2/T2)/M", @"M/L3", @"L3",
			     @"L", nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", @"0", @"0", @"0",
				   @"0", @"0", @"0", @"0", nil];
	image = @"state";
      }
      break;
    case 5:
      {
	// Data at some state (satured)
	titleList = [NSArray arrayWithObjects: _(@"Name"), @"p", @"T", @"u",
			     @"h", @"d", @"V", @"y", @"\%", nil];
	unitsList = [NSArray arrayWithObjects: @"nil", @"M/(T2*L)", @"O",
			     @"(M*L2/T2)/M", @"(M*L2/T2)/M", @"M/L3", @"L3",
			     @"L", @"\%", nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", @"0", @"0", @"0",
				   @"0", @"0", @"0", @"0", @"0", nil];
	image = @"state_x";
      }
      break;
    case 6:
      {
	// Data of a flow
	titleList = [NSArray arrayWithObjects: _(@"Name"), @"p", @"T", @"u",
			     @"h", @"d", @"A", @"v", @"y", nil];
	unitsList = [NSArray arrayWithObjects: @"nil", @"M/(T2*L)", @"O",
			     @"(M*L2/T2)/M", @"(M*L2/T2)/M", @"M/L3", @"L2",
			     @"L/T", @"L", nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", @"0", @"0", @"0",
				   @"0", @"0", @"0", @"0", @"0", nil];
	image = @"flow";
      }
      break;
    case 7:
      {
	// Data of a flow (satured)
	titleList = [NSArray arrayWithObjects: _(@"Name"), @"p", @"T", @"u",
			     @"h", @"d", @"A", @"v", @"y", @"\%", nil];
	unitsList = [NSArray arrayWithObjects: @"nil", @"M/(T2*L)", @"O",
			     @"(M*L2/T2)/M", @"(M*L2/T2)/M", @"M/L3", @"L2",
			     @"L/T", @"L", @"\%", nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", @"0", @"0", @"0",
				   @"0", @"0", @"0", @"0", @"0", @"0", nil];
	image = @"flow_x";
      }
      break;
    case 8:
      {
	// A change
	titleList = [NSArray arrayWithObjects: _(@"System i"), _(@"System f"),
			     @"m", nil];
	unitsList = [NSArray arrayWithObjects: @"nil", @"nil", @"M", 
			     nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", @"0", @"0",
				   nil];
	image = @"process_general";
      }
      break;
    case 9:
      {
	// A change of ideal gas
	titleList = [NSArray arrayWithObjects: _(@"System i"), _(@"System f"),
			     @"m", @"R", @"n", nil];
	unitsList = [NSArray arrayWithObjects: @"nil", @"nil", @"M", 
			     @"(M*L2/T2)/(M*O)", @"ad", nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", @"0", @"0", @"0",
				   @"0", nil];
	image = @"process_ideal_gas";
      }
      break;
      // Basic thermodynamic elements
    case 10:
      {
	// An adiabatic process
	titleList = [NSArray arrayWithObjects: _(@"Name"), @"m", @"dU", nil];
	unitsList = [NSArray arrayWithObjects: @"nil", @"M", @"M*L2/T2", nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", @"0", @"0", nil];
	image = @"adiabatic_process";
      }
      break;
    case 11:
      {
	// An isochoric process
	titleList = [NSArray arrayWithObjects: _(@"Name"), @"m", @"dU", nil];
	unitsList = [NSArray arrayWithObjects: @"nil", @"M", @"M*L2/T2", nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", @"0", @"0", nil];
	image = @"gas_v";
      }
      break;
    case 12:
      {
	// An isothermal process
	titleList = [NSArray arrayWithObjects: _(@"Name"), @"m", nil];
	unitsList = [NSArray arrayWithObjects: @"nil", @"M", nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", @"0", nil];
	image = @"gas_t";
      }
      break;
    case 13:
      {
	// An isobaric process
	titleList = [NSArray arrayWithObjects: _(@"Name"), @"m", @"dU", @"P", @"Vi", @"Vf", nil];
	unitsList = [NSArray arrayWithObjects: @"nil", @"M", @"M*L2/T2", @"M/(T2*L)", @"L3", @"L3", nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", @"0", @"0", @"0", @"0", @"0", nil];
	image = @"gas_p";
      }
      break;
    case 14:
      {
	// Sensible heat
	titleList = [NSArray arrayWithObjects: _(@"Process"), @"c", @"Ti", @"Tf",nil];
	unitsList = [NSArray arrayWithObjects: @"nil", @"L2/(T2*O)", @"O", @"O",nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", @"0", @"0", @"0", nil];
	image = @"sensible_heat";
      }
      break;
    case 15:
      {
	// Latent heat liquid-gas
	titleList = [NSArray arrayWithObjects: _(@"Process"), @"cv", nil];
	unitsList = [NSArray arrayWithObjects: @"nil", @"(M*L2/T2)/M", nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", @"0", nil];
	image = @"change_state_liquid_gas";
      }
      break;
    case 20:
      {
	// Heat out
	titleList = [NSArray arrayWithObjects: @"Q", nil];
	unitsList = [NSArray arrayWithObjects: @"M*L2/T2", nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", nil];
	image = @"heat_out";
      }
      break;
    case 21:
      {
	// Heat engine
	titleList = [NSArray arrayWithObjects: @"TH", @"TL", @"e", nil];
	unitsList = [NSArray arrayWithObjects: @"O", @"O", @"\%", nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", @"0", @"0", nil];
	image = @"heat_engine";
      }
      break;
    case 22:
      {
	// Refrigerator
	titleList = [NSArray arrayWithObjects: @"TH", @"TL", @"cop", nil];
	unitsList = [NSArray arrayWithObjects: @"O", @"O", @"\%", nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", @"0", @"0", nil];
	image = @"refrigerator";
      }
      break;
    case 23:
      {
	// Heat pump
	titleList = [NSArray arrayWithObjects: @"TH", @"TL", @"cop", nil];
	unitsList = [NSArray arrayWithObjects: @"O", @"O", @"\%", nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", @"0", @"0", nil];
	image = @"heat_pump";
      }
      break;
    case 24:
      {
	// General gas
	titleList = [NSArray arrayWithObjects: @"Pi", @"Vi", @"Ti", @"Pf",
			     @"Vf", @"Tf", nil];
	unitsList = [NSArray arrayWithObjects: @"M/(T2*L)", @"L3", @"O",
			     @"M/(T2*L)", @"L3", @"O", nil];
	dataList = [NSMutableArray arrayWithObjects: @"0", @"0", @"0", @"0",
				   @"0", @"0", nil];
	image = @"gas_pvt";
      }
      break;
    }

  return [NSArray arrayWithObjects: titleList, dataList, unitsList,
	   [NSNumber numberWithInt: tag], image, nil];
}

- (id) init
{
  self = [super init];

  unknowns = [NSMutableArray new];
  objectsNames = [NSMutableArray new];
  objectsIds = [NSMutableArray new];
  objectsContained = [NSMutableArray new];
  objectsChange = [NSMutableArray new];
  objectsDictionary = [NSMutableDictionary new];
  codApplied = [NSMutableArray new];
  objsWithApplied = [NSMutableArray new];
  heatAndWork = [NSMutableArray new];
  objectsBasicTherm = [NSMutableArray new];
  sensibleLatentHeat = [NSMutableArray new];
  codeOthers = [NSMutableArray new];

  // Set the name of messages file.
  [self setMessagesFile: @"thermodynamics"];

  return self;
}

- (void) dealloc
{
  [unknowns release];
  [objectsNames release];
  [objectsIds release];
  [objectsContained release];
  [objectsChange release];
  [objectsDictionary release];
  [codApplied release];
  [objsWithApplied release];
  [heatAndWork release];
  [objectsBasicTherm release];
  [sensibleLatentHeat release];
  [codeOthers release];
  [super dealloc];
}

- (void) solveProblemWithData: (NSMutableDictionary *)list
{
  NSUInteger errorNumber = 0, numberOfEquations = 0;
  BOOL error = NO;
  NSString *item;
  NSNumber *code, *type;
  NSArray *keys;
  NSEnumerator *enumerator;

  // To manage the number of applied elements (heat and work)
  NSNumber *ident;
  
  // Gravity and time
  varG = 0;
  gravityDat = 0;
  varT = 0;
  timeDat = 0;

  [objectsDictionary setDictionary: list];

  keys = [[NSArray alloc] initWithArray: [list allKeys]];
  enumerator = [keys objectEnumerator];

  error = [self searchUnknownsIn: objectsDictionary andStoreIn: unknowns];
  
  while ((code = [enumerator nextObject]) && !error)
    {
      NSMutableArray *values = [[objectsDictionary objectForKey: code]
				 objectForKey: @"Values"];
      
      type = [[objectsDictionary objectForKey: code]
	       objectForKey: @"Type"];
      
      if (error)
	break;

      // Count the amount of equations
      switch ([type intValue])
	{
	case 1:
	  {
	    // Reference system
	    if (![self isNumericDataTheString: [values objectAtIndex: 0]])
	      {
		gravityVar = [values objectAtIndex: 0];
		varG = 1;
	      }
	    else
	      {
		gravityDat = [[values objectAtIndex: 0] doubleValue];
		varG = 2;
	      }

	    if (![self isNumericDataTheString: [values objectAtIndex: 1]])
	      {
		timeVar = [values objectAtIndex: 1];
		varT = 1;
	      }
	    else
	      {
		timeDat = [[values objectAtIndex: 1] doubleValue];
		varT = 2;
	      }
	  }
	  break;
	case 2:
	case 3:
	  {
	    // Heat and Work
	    [heatAndWork addObject: code];
	  }
	  break;
	case 4 ... 7:
	  {
	    // State, state (satured), flow, flow (satured)
	    NSString *name = [values objectAtIndex: 0];

	    if ([self isEmptyTheName: name])
	      {
		error = YES;
		errorNumber = 0;
	      }
	    else
	      {
		if (![objectsNames containsObject: name])
		  {
		    [objectsNames addObject: name];
		    [objectsIds addObject: code];
		  }
		else
		  {
		    error = YES;
		    errorNumber = 1;
		  }
	      }
	  }
	  break;
	case 8:
	  {
	    // Change object
	    NSString *nameOne = [values objectAtIndex: 0];
	    NSString *nameTwo = [values objectAtIndex: 1];

	    if ([self isEmptyTheName: nameOne] ||
		[self isEmptyTheName: nameTwo])
	      {
		error = YES;
		errorNumber = 2;
	      }
	    else
	      {
		[objectsContained addObject: nameOne];
		[objectsContained addObject: nameTwo];
		[objectsChange addObject: code];
		[objsWithApplied addObject: code];
		numberOfEquations += 5;
	      }
	  }
	  break;
	case 9:
	  {
	    // Change object of ideal gas
	    NSUInteger changeType;
	    NSString *nameOne = [values objectAtIndex: 0];
	    NSString *nameTwo = [values objectAtIndex: 1];

	    if ([self isEmptyTheName: nameOne] ||
		[self isEmptyTheName: nameTwo])
	      {
		error = YES;
		errorNumber = 2;
	      }
	    else if ([unknowns containsObject: [values objectAtIndex: 4]])
	      {
		error = YES;
		errorNumber = 5;
	      }
	    else
	      {
		changeType = [[values objectAtIndex: 4] intValue];

		[objectsContained addObject: nameOne];
		[objectsContained addObject: nameTwo];
		[objectsChange addObject: code];
		[objsWithApplied addObject: code];

		if (changeType == 1)
		  {
		    numberOfEquations += 5;
		  }
		else
		  {
		    numberOfEquations += 5;
		  }
	      }
	  }
	  break;
	case 10 ... 13:
	case 21 ... 23:
	  {
	    // A basic thermodynamic process or
	    // a carnot cycle.
	    if ([type intValue] >= 10 && [type intValue] <= 13)
	      {
		NSString *name = [values objectAtIndex: 0];

		if (![self isEmptyTheName: name])
		  {
		    [objectsNames addObject: name];
		    [objectsIds addObject: code];
		  }
	      }

	    [objectsBasicTherm addObject: code];
	    [objsWithApplied addObject: code];

	    if ([type intValue] == 13)
	      {
		numberOfEquations += 2;
	      }
	    else if ([type intValue] >= 21 && [type intValue] <= 23)
	      {
		numberOfEquations += 3;
	      }
	    else
	      {
		numberOfEquations += 1;
	      }
	  }
	  break;
	case 14:
	case 15:
	  {
	    // Sensible and latent heat
	    NSString *name = [values objectAtIndex: 0];

	    if ([self isEmptyTheName: name])
	      {
		error = YES;
		errorNumber = 11;
	      }
	    else
	      {
		[objectsContained addObject: name];
		[sensibleLatentHeat addObject: code];
		numberOfEquations += 1;
	      }
	  }
	  break;
	case 20:
	  {
	    // Heat out
	    [heatAndWork addObject: code];
	  }
	  break;
	case 24:
	  {
	    numberOfEquations += 1;
	    [codeOthers addObject: code];
	  }
	  break;
	}

      if (error)
	break;

      // Search the applied sources or flows

      if ([type intValue] == 8 || [type intValue] == 9 ||
	  [type intValue] == 10 || [type intValue] == 11 ||
	  [type intValue] == 12 || [type intValue] == 13 ||
	  [type intValue] == 21 || [type intValue] == 22 ||
	  [type intValue] == 23)
	{
	  [self searchAppliedElementsAt: code andStoreIn: codApplied];
	  //NSLog([codApplied description]);
	}
    }

  // Check if the referenced objects exist.
  enumerator = [objectsContained objectEnumerator];

  while ((item = [enumerator nextObject]))
    {
      if (![objectsNames containsObject: item])
	{
	  error = YES;
	  errorNumber = 4;
	  break;
	}
    }

  // Check the applied objects at Change elements.
  NSUInteger x = 0;
  NSMutableArray *arrayApplied;
  
  enumerator = [objsWithApplied objectEnumerator];

  while ((item = [enumerator nextObject]))
    {
      arrayApplied = [codApplied objectAtIndex: x];
      ident = [[objectsDictionary objectForKey: item]
				 objectForKey: @"Type"];
      x += 1;

      switch ([ident intValue])
	{
	case 8:
	case 9:
	case 10 ... 13:
	case 21 ... 23:
	  {
	    if ([arrayApplied count] > 3)
	      {
		error = YES;
		errorNumber = 6;
		break;
	      }
	    else if ([arrayApplied count] == 3)
	      {
		NSUInteger tp1, tp2, tp3;

		tp1 = [[[objectsDictionary objectForKey:
			         [arrayApplied objectAtIndex: 0]]
				 objectForKey: @"Type"] intValue];

		tp2 = [[[objectsDictionary objectForKey:
			         [arrayApplied objectAtIndex: 1]]
				 objectForKey: @"Type"] intValue];

		tp3 = [[[objectsDictionary objectForKey:
			         [arrayApplied objectAtIndex: 2]]
				 objectForKey: @"Type"] intValue];

		if (!(tp1 == 20 && tp2 == 2 && tp3 == 3) &&
		    !(tp1 == 20 && tp2 == 3 && tp3 == 2) &&
		    !(tp1 == 2 && tp2 == 20 && tp3 == 3) &&
		    !(tp1 == 2 && tp2 == 3 && tp3 == 20) &&
		    !(tp1 == 3 && tp2 == 20 && tp3 == 2) &&
		    !(tp1 == 3 && tp2 == 2 && tp3 == 20))
		  {
		    error = YES;
		    errorNumber = 7;
		    break;
		  }
		else if ([ident intValue] == 10 ||
			 [ident intValue] == 11)
		  {
		    error = YES;
		    errorNumber = 8;
		    break;
		  }
	      }
	    else if ([arrayApplied count] == 2)
	      {
		NSUInteger tp1, tp2;

		tp1 = [[[objectsDictionary objectForKey:
			         [arrayApplied objectAtIndex: 0]]
				 objectForKey: @"Type"] intValue];

		tp2 = [[[objectsDictionary objectForKey:
			         [arrayApplied objectAtIndex: 1]]
				 objectForKey: @"Type"] intValue];

		if (!(tp1 == 2 && tp2 == 3) && !(tp1 == 3 && tp2 == 2) &&
		    !(tp1 == 2 && tp2 == 20) && !(tp1 == 20 && tp2 == 2) &&
		    !(tp1 == 3 && tp2 == 20) && !(tp1 == 20 && tp2 == 3))
		  {
		    error = YES;
		    errorNumber = 7;
		    break;
		  }
		else if ([ident intValue] == 10 &&
			 (tp1 == 2 || tp2 == 2 || tp1 == 20 || tp2 == 20))
		  {
		    error = YES;
		    errorNumber = 9;
		    break;
		  }
		else if ([ident intValue] == 11 &&
			 (tp1 == 3 || tp2 == 3))
		  {
		    error = YES;
		    errorNumber = 10;
		    break;
		  }
		else if ([ident intValue] >= 21 && [ident intValue] <= 23)
		  {
		    error = YES;
		    errorNumber = 13;
		    break;
		  }
	      }
	    else if ([arrayApplied count] == 1)
	      {
		NSUInteger tp;
		
		tp = [[[objectsDictionary objectForKey:
  			         [arrayApplied objectAtIndex: 0]]
				 objectForKey: @"Type"] intValue];

		if (tp != 2 && tp != 3 && tp != 20)
		  {
		    error = YES;
		    errorNumber = 7;
		    break;
		  }
		else if ([ident intValue] == 10 &&
			 (tp == 2 || tp == 20))
		  {
		    error = YES;
		    errorNumber = 9;
		    break;
		  }
		else if ([ident intValue] == 11 && tp == 3)
		  {
		    error = YES;
		    errorNumber = 10;
		    break;
		  }
		else if ([ident intValue] >= 21 && [ident intValue] <= 23)
		  {
		    error = YES;
		    errorNumber = 13;
		    break;
		  }
	      }
	    else if ([arrayApplied count] == 0)
	      {
		if ([ident intValue] >= 21 && [ident intValue] <= 23)
		  {
		    error = YES;
		    errorNumber = 13;
		    break;
		  }
	      }
	  }
	  break;
	}
    }

  // Check objects Change
  NSNumber *idObj;
  NSString *name;

  enumerator = [objectsChange objectEnumerator];

  while ((code = [enumerator nextObject]))
    {
      ident = [[objectsDictionary objectForKey: code]
				 objectForKey: @"Type"];

      NSMutableArray *values = [[objectsDictionary objectForKey: code]
				 objectForKey: @"Values"];

      // Get the name of the element
      name = [[values objectAtIndex: 0] description];
      
      // Get the id of the element
      idObj = [objectsIds objectAtIndex: [objectsNames indexOfObject: name]];
      
      // Get the data of the element
      type = [[objectsDictionary objectForKey: idObj]
			objectForKey: @"Type"];

      if ( !([type intValue] == 4 || [type intValue] == 5 ||
	     [type intValue] == 6 || [type intValue] == 7) )
	{
	  error = YES;
	  errorNumber = 13;
	  break;
	}
    }

  // Check objects Sensible and Latent heat
  enumerator = [sensibleLatentHeat objectEnumerator];

  while ((code = [enumerator nextObject]))
    {
      ident = [[objectsDictionary objectForKey: code]
				 objectForKey: @"Type"];

      NSMutableArray *values = [[objectsDictionary objectForKey: code]
				 objectForKey: @"Values"];

      // Get the name of the element
      name = [[values objectAtIndex: 0] description];
      
      // Get the id of the element
      idObj = [objectsIds objectAtIndex: [objectsNames indexOfObject: name]];
      
      // Get the data of the element
      type = [[objectsDictionary objectForKey: idObj]
			objectForKey: @"Type"];
      
      if ( !([type intValue] == 10 || [type intValue] == 11 ||
	     [type intValue] == 13) )
	{
	  error = YES;
	  errorNumber = 12;
	  break;
	}
    }

  // Final verifications
  if (!error)
    {
      if ([unknowns count] == numberOfEquations && numberOfEquations > 0)
	{
	  [self makeSystem];
	}
      else
	{
	  [self writeMessageAtIndex: 3];
	  //printf("%d \n", numberOfEquations);
	  //NSLog([unknowns description]);
	}
    }
  else
    {
      [self writeMessageAtIndex: errorNumber];
    }
  
}

- (void) buildSystemWithUnknowns: (const gsl_vector *)vrs
                    forFunctions: (gsl_vector *)func
{
  int nEqu = 0;
  NSUInteger xCount; // tIndex, gIndex, 
  NSEnumerator *enumerator, *counter;
  NSNumber *object, *changeType = nil, *heatType, *typeOne; // *type,
  NSMutableArray *data;
  // double tf, gf;

  // For data element
  NSString *nameOne; //, *nameTwo;
  NSNumber *idOne, *idTwo;
  NSMutableArray *dataOne; //, *dataTwo;

  // Get data for gravity and time
  // if (varG == 1)
  //   {
  //     gIndex = [unknowns indexOfObject: gravityVar];
  //     gf = gsl_vector_get (vrs, gIndex);
  //   }
  // else
  //   {
  //     if (varG == 2)
  // 	{
  //         gf = gravityDat;
  // 	}
  //     else
  // 	{
  //         gf = 0;
  // 	}
  //   }

  // if (varT == 1)
  //   {
  //     tIndex = [unknowns indexOfObject: timeVar];
  //     tf = gsl_vector_get (vrs, tIndex);
  //   }
  // else
  //   {
  //     if (varT == 2)
  // 	{
  //         tf = timeDat;
  // 	}
  //     else
  // 	{
  //         tf = 0;
  // 	}
  //   }

  // Write equations for elements Change.
  // enumerator = [objectsChange objectEnumerator];
  
  // while ((object = [enumerator nextObject]))
        // {
    //   double m = 0, R = 0, n = 0, heat = 0, work = 0, temOne = 0, temTwo = 0;
    //   double lostheat = 0, pressureOne = 0, pressureTwo = 0, VOne = 0, VTwo = 0;
    //   double uOne = 0, uTwo = 0, hOne = 0, hTwo = 0;
    //   double dOne = 0, dTwo = 0, yOne = 0, yTwo = 0;
    //   double percentOne = 0, percentTwo = 0;
    //   BOOL pOne = NO, pTwo = NO;

    //   data = [[objectsDictionary objectForKey: object] objectForKey: @"Values"];
    //   changeType = [[objectsDictionary objectForKey: object]
    // 		     objectForKey: @"Type"];

    //   // Get mass data
    //   if (![unknowns containsObject: [data objectAtIndex: 2]])
    // 	{
    // 	  m = [[data objectAtIndex: 2] doubleValue];
    // 	}
    //   else
    // 	{
    // 	  int k = [unknowns indexOfObject: [data objectAtIndex: 2]];
    // 	  m = gsl_vector_get (vrs, k);
    // 	}
      
    //   if ([changeType intValue] == 9)
    // 	{
    // 	  // Get R data
    // 	  if (![unknowns containsObject: [data objectAtIndex: 3]])
    // 	    {
    // 	      R = [[data objectAtIndex: 3] doubleValue];
    // 	    }
    // 	  else
    // 	    {
    // 	      int k = [unknowns indexOfObject: [data objectAtIndex: 3]];
    // 	      R = gsl_vector_get (vrs, k);
    // 	    }
	  
    // 	  // Get n data
    // 	  if (![unknowns containsObject: [data objectAtIndex: 4]])
    // 	    {
    // 	      n = [[data objectAtIndex: 4] doubleValue];
    // 	    }
    // 	}
      
    //   // Get the names of the elements
    //   nameOne = [[data objectAtIndex: 0] description];
    //   nameTwo = [[data objectAtIndex: 1] description];
      
    //   // Get the ids of the elements
    //   idOne = [objectsIds objectAtIndex: [objectsNames indexOfObject: nameOne]];
    //   idTwo = [objectsIds objectAtIndex: [objectsNames indexOfObject: nameTwo]];
      
    //   // Get the data of the elements
    //   dataOne = [[objectsDictionary objectForKey: idOne]
    // 			objectForKey: @"Values"];
    //   dataTwo = [[objectsDictionary objectForKey: idTwo]
    // 			objectForKey: @"Values"];
      
    //   // Get the data of first object
    //   type = [[objectsDictionary objectForKey: idOne] objectForKey: @"Type"];
      
    //   switch ([type intValue])
    // 	{
    // 	case 4:
    // 	case 5:
    // 	  {
    // 	    if (![unknowns containsObject: [dataOne objectAtIndex: 1]])
    // 	      {
    // 		pressureOne = [[dataOne objectAtIndex: 1] doubleValue];
    // 	      }
    // 	    else
    // 	      {
    // 		int k = [unknowns indexOfObject: [dataOne objectAtIndex: 1]];
    // 		pressureOne = gsl_vector_get (vrs, k);
    // 	      }

    // 	    if (n != 1)
    // 	      {
    // 		if (![unknowns containsObject: [dataOne objectAtIndex: 2]])
    // 		  {
    // 		    temOne = [[dataOne objectAtIndex: 2] doubleValue];
    // 		  }
    // 		else
    // 		  {
    // 		    int k = [unknowns indexOfObject: [dataOne objectAtIndex: 2]];
    // 		    temOne = gsl_vector_get (vrs, k);
    // 		  }
    // 	      }
	    
    // 	    if (n != 1)
    // 	      {
    // 		if (![unknowns containsObject: [dataOne objectAtIndex: 3]])
    // 		  {
    // 		    uOne = [[dataOne objectAtIndex: 3] doubleValue];
    // 		  }
    // 		else
    // 		  {
    // 		    int k = [unknowns indexOfObject: [dataOne objectAtIndex: 3]];
    // 		    uOne = gsl_vector_get (vrs, k);
    // 		  }
    // 	      }
	    
    // 	    if (n != 1)
    // 	      {
    // 		if (![unknowns containsObject: [dataOne objectAtIndex: 4]])
    // 		  {
    // 		    hOne = [[dataOne objectAtIndex: 4] doubleValue];
    // 		  }
    // 		else
    // 		  {
    // 		    int k = [unknowns indexOfObject: [dataOne objectAtIndex: 4]];
    // 		    hOne = gsl_vector_get (vrs, k);
    // 		  }
    // 	      }
	    
    // 	    if (![unknowns containsObject: [dataOne objectAtIndex: 5]])
    // 	      {
    // 		dOne = [[dataOne objectAtIndex: 5] doubleValue];
    // 	      }
    // 	    else
    // 	      {
    // 		int k = [unknowns indexOfObject: [dataOne objectAtIndex: 5]];
    // 		dOne = gsl_vector_get (vrs, k);
    // 	      }
	    
    // 	    if (![unknowns containsObject: [dataOne objectAtIndex: 6]])
    // 	      {
    // 		VOne = [[dataOne objectAtIndex: 6] doubleValue];
    // 	      }
    // 	    else
    // 	      {
    // 		int k = [unknowns indexOfObject: [dataOne objectAtIndex: 6]];
    // 		VOne = gsl_vector_get (vrs, k);
    // 	      }
	    
    // 	    if (![unknowns containsObject: [dataOne objectAtIndex: 7]])
    // 	      {
    // 		yOne = [[dataOne objectAtIndex: 7] doubleValue];
    // 	      }
    // 	    else
    // 	      {
    // 		int k = [unknowns indexOfObject: [dataOne objectAtIndex: 7]];
    // 		yOne = gsl_vector_get (vrs, k);
    // 	      }

    // 	    if ([type intValue] == 5)
    // 	      {
    // 		pOne = YES;

    // 		if (![unknowns containsObject: [dataOne objectAtIndex: 8]])
    // 		  {
    // 		    percentOne = [[dataOne objectAtIndex: 8] doubleValue];
    // 		  }
    // 		else
    // 		  {
    // 		    int k = [unknowns indexOfObject: [dataOne objectAtIndex: 8]];
    // 		    percentOne = gsl_vector_get (vrs, k);
    // 		  }
    // 	      }
    // 	  }
    // 	  break;
    // 	}
      
    //   // Get the data of second object
    //   type = [[objectsDictionary objectForKey: idTwo] objectForKey: @"Type"];
      
    //   switch ([type intValue])
    // 	{
    // 	case 4:
    // 	  {
    // 	    if (![unknowns containsObject: [dataTwo objectAtIndex: 1]])
    // 	      {
    // 		pressureTwo = [[dataTwo objectAtIndex: 1] doubleValue];
    // 	      }
    // 	    else
    // 	      {
    // 		int k = [unknowns indexOfObject: [dataTwo objectAtIndex: 1]];
    // 		pressureTwo = gsl_vector_get (vrs, k);
    // 	      }

    // 	    if (n != 1)
    // 	      {
    // 		if (![unknowns containsObject: [dataTwo objectAtIndex: 2]])
    // 		  {
    // 		    temTwo = [[dataTwo objectAtIndex: 2] doubleValue];
    // 		  }
    // 		else
    // 		  {
    // 		    int k = [unknowns indexOfObject: [dataTwo objectAtIndex: 2]];
    // 		    temTwo = gsl_vector_get (vrs, k);
    // 		  }
    // 	      }
	    
    // 	    if (n != 1)
    // 	      {
    // 		if (![unknowns containsObject: [dataTwo objectAtIndex: 3]])
    // 		  {
    // 		    uTwo = [[dataTwo objectAtIndex: 3] doubleValue];
    // 		  }
    // 		else
    // 		  {
    // 		    int k = [unknowns indexOfObject: [dataTwo objectAtIndex: 3]];
    // 		    uTwo = gsl_vector_get (vrs, k);
    // 		  }
    // 	      }
	    
    // 	    if (n != 1)
    // 	      {
    // 		if (![unknowns containsObject: [dataTwo objectAtIndex: 4]])
    // 		  {
    // 		    hTwo = [[dataTwo objectAtIndex: 4] doubleValue];
    // 		  }
    // 		else
    // 		  {
    // 		    int k = [unknowns indexOfObject: [dataTwo objectAtIndex: 4]];
    // 		    hTwo = gsl_vector_get (vrs, k);
    // 		  }
    // 	      }
	    
    // 	    if (![unknowns containsObject: [dataTwo objectAtIndex: 5]])
    // 	      {
    // 		dTwo = [[dataTwo objectAtIndex: 5] doubleValue];
    // 	      }
    // 	    else
    // 	      {
    // 		int k = [unknowns indexOfObject: [dataTwo objectAtIndex: 5]];
    // 		dTwo = gsl_vector_get (vrs, k);
    // 	      }
	    
    // 	    if (![unknowns containsObject: [dataTwo objectAtIndex: 6]])
    // 	      {
    // 		VTwo = [[dataTwo objectAtIndex: 6] doubleValue];
    // 	      }
    // 	    else
    // 	      {
    // 		int k = [unknowns indexOfObject: [dataTwo objectAtIndex: 6]];
    // 		VTwo = gsl_vector_get (vrs, k);
    // 	      }
	    
    // 	    if (![unknowns containsObject: [dataTwo objectAtIndex: 7]])
    // 	      {
    // 		yTwo = [[dataTwo objectAtIndex: 7] doubleValue];
    // 	      }
    // 	    else
    // 	      {
    // 		int k = [unknowns indexOfObject: [dataTwo objectAtIndex: 7]];
    // 		yTwo = gsl_vector_get (vrs, k);
    // 	      }

    // 	    if ([type intValue] == 5)
    // 	      {
    // 		pTwo = YES;

    // 		if (![unknowns containsObject: [dataTwo objectAtIndex: 8]])
    // 		  {
    // 		    percentTwo = [[dataTwo objectAtIndex: 8] doubleValue];
    // 		  }
    // 		else
    // 		  {
    // 		    int k = [unknowns indexOfObject: [dataTwo objectAtIndex: 8]];
    // 		    percentTwo = gsl_vector_get (vrs, k);
    // 		  }
    // 	      }
    // 	  }
    // 	  break;
    // 	}
      
    //   // Check applied heat and work (if any)
    //   xCount = [objsWithApplied indexOfObject: object];
      
    //   counter = [[codApplied objectAtIndex: xCount] objectEnumerator];
      
    //   while ((idOne = [counter nextObject]))
    // 	{
    // 	  // Get the data of the element
    // 	  dataOne = [[objectsDictionary objectForKey: idOne]
    // 			objectForKey: @"Values"];
    // 	  typeOne = [[objectsDictionary objectForKey: idOne]
    // 			objectForKey: @"Type"];

    // 	  if ([typeOne intValue] == 2)
    // 	    {
    // 	      if (![unknowns containsObject: [dataOne objectAtIndex: 0]])
    // 		{
    // 		  heat = [[dataOne objectAtIndex: 0] doubleValue];
    // 		}
    // 	      else
    // 		{
    // 		  int k = [unknowns indexOfObject: [dataOne objectAtIndex: 0]];
    // 		  heat = gsl_vector_get (vrs, k);
    // 		}
    // 	    }
    // 	  else if ([typeOne intValue] == 3)
    // 	    {
    // 	      if (![unknowns containsObject: [dataOne objectAtIndex: 0]])
    // 		{
    // 		  work = [[dataOne objectAtIndex: 0] doubleValue];
    // 		}
    // 	      else
    // 		{
    // 		  int k = [unknowns indexOfObject: [dataOne objectAtIndex: 0]];
    // 		  work = gsl_vector_get (vrs, k);
    // 		}
    // 	    }
    // 	  else
    // 	    {
    // 	      if (![unknowns containsObject: [dataOne objectAtIndex: 0]])
    // 		{
    // 		  lostheat = [[dataOne objectAtIndex: 0] doubleValue];
    // 		}
    // 	      else
    // 		{
    // 		  int k = [unknowns indexOfObject: [dataOne objectAtIndex: 0]];
    // 		  lostheat = gsl_vector_get (vrs, k);
    // 		}
    // 	    }
    // 	}

    //   // Write the equations

    //   // A general process.
    //   if ([changeType intValue] == 8)
    // 	{
    // 	  gsl_vector_set (func, nEqu, (uTwo + gf*yTwo) - (uOne + gf*yOne) +
    // 			  work - (heat - lostheat) );
    // 	  gsl_vector_set (func, nEqu + 1, uOne + pressureOne/dOne - hOne);
    // 	  gsl_vector_set (func, nEqu + 2, uTwo + pressureTwo/dTwo - hTwo);
    //   	  gsl_vector_set (func, nEqu + 3, dTwo*VTwo - dOne*VOne);
    // 	  gsl_vector_set (func, nEqu + 4, m - dOne*VOne);
    // 	  nEqu += 5;
    // 	}
    //   // An ideal gas process.
    //   else
    // 	{
    // 	  if (n == 0) // Isobaric process.
    // 	    {
    // 	      gsl_vector_set (func, nEqu, (uTwo + gf*yTwo) - (uOne + gf*yOne) +
    // 			      work - (heat - lostheat) );
    // 	      gsl_vector_set (func, nEqu + 1, work -
    // 			      pressureOne*(VTwo - VOne));
    // 	      gsl_vector_set (func, nEqu + 2, temTwo/temOne -
    // 			      pow(VOne/VTwo, n - 1));
	      
    // 	      gsl_vector_set (func, nEqu + 3, dTwo*VTwo - dOne*VOne);
    // 	      gsl_vector_set (func, nEqu + 4, m - dOne*VOne);
    // 	      gsl_vector_set (func, nEqu + 5, uOne + pressureOne/dOne - hOne);
    // 	      gsl_vector_set (func, nEqu + 6, uTwo + pressureTwo/dTwo - hTwo);
    // 	      gsl_vector_set (func, nEqu + 7, pressureOne - pressureTwo);
    // 	      nEqu += 8;
    // 	    }
    // 	  else if (n == 1) // Isothermal process, hentalpy and temperature
    // 	                   // are not take into account.
    // 	    {
    // 	      gsl_vector_set (func, nEqu, (gf*yTwo - gf*yOne) + work -
    // 			      (heat - lostheat) );
    // 	      gsl_vector_set (func, nEqu + 1, work -
    // 			      pressureTwo*VTwo*log(VTwo/VOne));
    // 	      gsl_vector_set (func, nEqu + 2, pressureTwo*VTwo -
    // 			      pressureOne*VOne);
	      
    // 	      gsl_vector_set (func, nEqu + 3, dTwo*VTwo - dOne*VOne);
    // 	      gsl_vector_set (func, nEqu + 4, m - dOne*VOne);
    // 	      gsl_vector_set (func, nEqu + 5, temOne - temTwo);
    // 	      gsl_vector_set (func, nEqu + 6, hOne - hTwo);
    // 	      gsl_vector_set (func, nEqu + 7, uOne - uTwo);
    // 	      nEqu += 8;
    // 	    }
    // 	  else if (n == -1) // n = Infinitum (isometric process).
    // 	    {
    // 	      gsl_vector_set (func, nEqu, (uTwo + gf*yTwo) - (uOne + gf*yOne) -
    // 			      (heat - lostheat) );
    // 	      gsl_vector_set (func, nEqu + 1, temTwo/temOne -
    // 			      pow(pressureTwo/pressureOne, (n - 1)/n));
	      
    // 	      gsl_vector_set (func, nEqu + 2, dTwo - dOne);
    // 	      gsl_vector_set (func, nEqu + 3, m - dOne*VOne);
    // 	      gsl_vector_set (func, nEqu + 4, uOne + pressureOne/dOne - hOne);
    // 	      gsl_vector_set (func, nEqu + 5, uTwo + pressureTwo/dTwo - hTwo);
    // 	      gsl_vector_set (func, nEqu + 6, VOne - VTwo);
    // 	      nEqu += 7;
    // 	    }
    // 	  else // Any other polytropic process.
    // 	    {
    // 	      gsl_vector_set (func, nEqu, (uTwo + gf*yTwo) - (uOne + gf*yOne) +
    // 			      work - (heat - lostheat) );
    // 	      gsl_vector_set (func, nEqu + 1, work -
    // 			      (pressureTwo*VTwo - pressureOne*VOne)/(1 - n));
    // 	      gsl_vector_set (func, nEqu + 2, pressureTwo*pow(VTwo, n) -
    // 			      pressureOne*pow(VOne, n));
	      
    // 	      gsl_vector_set (func, nEqu + 3, dTwo*VTwo - dOne*VOne);
    // 	      gsl_vector_set (func, nEqu + 4, m - dOne*VOne);
    // 	      gsl_vector_set (func, nEqu + 5, uOne + pressureOne/dOne - hOne);
    // 	      gsl_vector_set (func, nEqu + 6, uTwo + pressureTwo/dTwo - hTwo);
    // 	      gsl_vector_set (func, nEqu + 7, temTwo/temOne -
    // 			      pow(VOne/VTwo, n - 1));
    // 	      nEqu += 8;
    // 	    }
    // 	}
      

    // }

  // Write equations for basic thermodynamics elements.
  enumerator = [objectsBasicTherm objectEnumerator];
  
  while ((object = [enumerator nextObject]))
    {
      double dU = 0, heat = 0, lostheat = 0, work = 0;
      double P = 0, Vi = 0, Vf = 0;
      double th = 0, tl = 0, ef = 0;

      data = [[objectsDictionary objectForKey: object] objectForKey: @"Values"];
      changeType = [[objectsDictionary objectForKey: object]
		     objectForKey: @"Type"];

      // If not an isothermal process, get dU
      if ([changeType intValue] != 12)
	{
	  if (![unknowns containsObject: [data objectAtIndex: 2]])
	    {
	      dU = [[data objectAtIndex: 2] doubleValue];
	    }
	  else
	    {
	      int k = [unknowns indexOfObject: [data objectAtIndex: 2]];
	      dU = gsl_vector_get (vrs, k);
	    }
	}

      // If an isochoric process get P, Vi, Vf
      if ([changeType intValue] == 13)
	{
	  if (![unknowns containsObject: [data objectAtIndex: 3]])
	    {
	      P = [[data objectAtIndex: 3] doubleValue];
	    }
	  else
	    {
	      int k = [unknowns indexOfObject: [data objectAtIndex: 3]];
	      P = gsl_vector_get (vrs, k);
	    }

	  if (![unknowns containsObject: [data objectAtIndex: 4]])
	    {
	      Vi = [[data objectAtIndex: 4] doubleValue];
	    }
	  else
	    {
	      int k = [unknowns indexOfObject: [data objectAtIndex: 4]];
	      Vi = gsl_vector_get (vrs, k);
	    }

	  if (![unknowns containsObject: [data objectAtIndex: 5]])
	    {
	      Vf = [[data objectAtIndex: 5] doubleValue];
	    }
	  else
	    {
	      int k = [unknowns indexOfObject: [data objectAtIndex: 5]];
	      Vf = gsl_vector_get (vrs, k);
	    }
	}

      // Get data of cycles elements.
      if ([changeType intValue] >= 21 && [changeType intValue] <= 23)
	{
	  if (![unknowns containsObject: [data objectAtIndex: 0]])
	    {
	      th = [[data objectAtIndex: 0] doubleValue];
	    }
	  else
	    {
	      int k = [unknowns indexOfObject: [data objectAtIndex: 0]];
	      th = gsl_vector_get (vrs, k);
	    }

	  if (![unknowns containsObject: [data objectAtIndex: 1]])
	    {
	      tl = [[data objectAtIndex: 1] doubleValue];
	    }
	  else
	    {
	      int k = [unknowns indexOfObject: [data objectAtIndex: 1]];
	      tl = gsl_vector_get (vrs, k);
	    }

	  if (![unknowns containsObject: [data objectAtIndex: 2]])
	    {
	      ef = [[data objectAtIndex: 2] doubleValue];
	    }
	  else
	    {
	      int k = [unknowns indexOfObject: [data objectAtIndex: 2]];
	      ef = gsl_vector_get (vrs, k);
	    }
	}

      // Check applied heat and work (if any)
      xCount = [objsWithApplied indexOfObject: object];

      counter = [[codApplied objectAtIndex: xCount] objectEnumerator];
      
      while ((idOne = [counter nextObject]))
	{
	  // Get the data of the element
	  dataOne = [[objectsDictionary objectForKey: idOne]
			objectForKey: @"Values"];
	  typeOne = [[objectsDictionary objectForKey: idOne]
			objectForKey: @"Type"];

	  if ([typeOne intValue] == 2)
   	    {
	      if (![unknowns containsObject: [dataOne objectAtIndex: 0]])
		{
		  heat = [[dataOne objectAtIndex: 0] doubleValue];
		}
	      else
		{
		  int k = [unknowns indexOfObject: [dataOne objectAtIndex: 0]];
		  heat = gsl_vector_get (vrs, k);
		}
	    }
	  else if ([typeOne intValue] == 3)
	    {
	      if (![unknowns containsObject: [dataOne objectAtIndex: 0]])
		{
		  work = [[dataOne objectAtIndex: 0] doubleValue];
		}
	      else
		{
		  int k = [unknowns indexOfObject: [dataOne objectAtIndex: 0]];
		  work = gsl_vector_get (vrs, k);
		}
	    }
	  else
	    {
	      if (![unknowns containsObject: [dataOne objectAtIndex: 0]])
		{
		  lostheat = [[dataOne objectAtIndex: 0] doubleValue];
		}
	      else
		{
		  int k = [unknowns indexOfObject: [dataOne objectAtIndex: 0]];
		  lostheat = gsl_vector_get (vrs, k);
		}
	    }
	}

      // Write the equation
      if ([changeType intValue] == 13)
	{
	  gsl_vector_set (func, nEqu, heat - lostheat - (dU + work) );
	  gsl_vector_set (func, nEqu + 1, work - P*(Vf - Vi) ); 
	  nEqu += 2;
	}
      else if ([changeType intValue] == 21)
	{
	  gsl_vector_set (func, nEqu, heat - lostheat - work );
	  gsl_vector_set (func, nEqu + 1, ef/100 - work/heat );
	  gsl_vector_set (func, nEqu + 2, 1 - tl/th - ef/100 );
	  nEqu += 3;
	}
      else if ([changeType intValue] == 22)
	{
	  gsl_vector_set (func, nEqu, heat - lostheat - work );
	  gsl_vector_set (func, nEqu + 1, ef - lostheat/work );
	  gsl_vector_set (func, nEqu + 2, tl/(th - tl) - ef );
	  nEqu += 3;
	}
      else if ([changeType intValue] == 23)
	{
	  gsl_vector_set (func, nEqu, heat - lostheat - work );
	  gsl_vector_set (func, nEqu + 1, ef - heat/work );
	  gsl_vector_set (func, nEqu + 2, th/(th - tl) - ef );
	  nEqu += 3;
	}
      else
	{
	  gsl_vector_set (func, nEqu, heat - lostheat - (dU + work) );
	  nEqu += 1;
	}
    }

  // Write equations to Sensible and Latent heat.
  enumerator = [sensibleLatentHeat objectEnumerator];
  
  while ((object = [enumerator nextObject]))
    {
      double c = 0, Ti = 0, Tf = 0, m = 0, heat = 0, lostheat = 0;

      data = [[objectsDictionary objectForKey: object] objectForKey: @"Values"];
      heatType = [[objectsDictionary objectForKey: object]
		     objectForKey: @"Type"];

      // Get c data
      if (![unknowns containsObject: [data objectAtIndex: 1]])
	{
	  c = [[data objectAtIndex: 1] doubleValue];
	}
      else
	{
	  int k = [unknowns indexOfObject: [data objectAtIndex: 1]];
	  c = gsl_vector_get (vrs, k);
	}
	
      if ([heatType intValue] == 14)
	{
	  // Get Ti data
	  if (![unknowns containsObject: [data objectAtIndex: 2]])
	    {
	      Ti = [[data objectAtIndex: 2] doubleValue];
	    }
	  else
	    {
	      int k = [unknowns indexOfObject: [data objectAtIndex: 2]];
	      Ti = gsl_vector_get (vrs, k);
	    }

	  // Get Tf data
	  if (![unknowns containsObject: [data objectAtIndex: 3]])
	    {
	      Tf = [[data objectAtIndex: 3] doubleValue];
	    }
	  else
	    {
	      int k = [unknowns indexOfObject: [data objectAtIndex: 3]];
	      Tf = gsl_vector_get (vrs, k);
	    }
	}

      // Get the name of the element
      nameOne = [[data objectAtIndex: 0] description];
      
      // Get the id of the element
      idOne = [objectsIds objectAtIndex: [objectsNames indexOfObject: nameOne]];
      
      // Get the data of the element
      dataOne = [[objectsDictionary objectForKey: idOne]
			objectForKey: @"Values"];

      // Get mass data
      if (![unknowns containsObject: [dataOne objectAtIndex: 1]])
	{
	  m = [[dataOne objectAtIndex: 1] doubleValue];
	}
      else
	{
	  int k = [unknowns indexOfObject: [dataOne objectAtIndex: 1]];
	  m = gsl_vector_get (vrs, k);
	}

      // Check applied heat (if any)
      xCount = [objsWithApplied indexOfObject: idOne];

      counter = [[codApplied objectAtIndex: xCount] objectEnumerator];
      
      while ((idTwo = [counter nextObject]))
	{
	  // Get the data of the element
	  dataOne = [[objectsDictionary objectForKey: idTwo]
			objectForKey: @"Values"];
	  typeOne = [[objectsDictionary objectForKey: idTwo]
			objectForKey: @"Type"];

	  if ([typeOne intValue] == 2)
	    {
	      if (![unknowns containsObject: [dataOne objectAtIndex: 0]])
		{
		  heat = [[dataOne objectAtIndex: 0] doubleValue];
		}
	      else
		{
		  int k = [unknowns indexOfObject: [dataOne objectAtIndex: 0]];
		  heat = gsl_vector_get (vrs, k);
		}
	    }
	  else if ([typeOne intValue] == 20)
	    {
	      if (![unknowns containsObject: [dataOne objectAtIndex: 0]])
		{
		  lostheat = [[dataOne objectAtIndex: 0] doubleValue];
		}
	      else
		{
		  int k = [unknowns indexOfObject: [dataOne objectAtIndex: 0]];
		  lostheat = gsl_vector_get (vrs, k);
		}
	    }
	}

      // Write the equation
      if ([heatType intValue] == 14)
	{
	  gsl_vector_set (func, nEqu, heat - lostheat - m*c*(Tf - Ti) );
	}
      else
	{
	  gsl_vector_set (func, nEqu, heat - lostheat - m*c );
	}

      nEqu += 1;

    }

  // Objects contained in codeOthers
  enumerator = [codeOthers objectEnumerator];
  
  while ((object = [enumerator nextObject]))
    {
      typeOne = [[objectsDictionary objectForKey: object]
			objectForKey: @"Type"];
      data = [[objectsDictionary objectForKey: object]
			      objectForKey: @"Values"];
      
      switch ([typeOne intValue])
	{
	  case 24:
	  {
	    // Ideal gas
	    double Pi, Vi, Ti, Pf, Vf, Tf; 
	    
	    if (![unknowns containsObject: [data objectAtIndex: 0]])
	      {
		Pi = [[data objectAtIndex: 0] doubleValue];
	      }
	    else
	      {
		int k = [unknowns indexOfObject: [data objectAtIndex: 0]];
		Pi = gsl_vector_get (vrs, k);
	      }
	    
	    if (![unknowns containsObject: [data objectAtIndex: 1]])
	      {
		Vi = [[data objectAtIndex: 1] doubleValue];
	      }
	    else
	      {
		int k = [unknowns indexOfObject: [data objectAtIndex: 1]];
		Vi = gsl_vector_get (vrs, k);
	      }
	    
	    if (![unknowns containsObject: [data objectAtIndex: 2]])
	      {
		Ti = [[data objectAtIndex: 2] doubleValue];
	      }
	    else
	      {
		int k = [unknowns indexOfObject: [data objectAtIndex: 2]];
		Ti = gsl_vector_get (vrs, k);
	      }
	    
	    if (![unknowns containsObject: [data objectAtIndex: 3]])
	      {
		Pf = [[data objectAtIndex: 3] doubleValue];
	      }
	    else
	      {
		int k = [unknowns indexOfObject: [data objectAtIndex: 3]];
		Pf = gsl_vector_get (vrs, k);
	      }
	    
	    if (![unknowns containsObject: [data objectAtIndex: 4]])
	      {
		Vf = [[data objectAtIndex: 4] doubleValue];
	      }
	    else
	      {
		int k = [unknowns indexOfObject: [data objectAtIndex: 4]];
		Vf = gsl_vector_get (vrs, k);
	      }
	    
	    if (![unknowns containsObject: [data objectAtIndex: 5]])
	      {
		Tf = [[data objectAtIndex: 5] doubleValue];
	      }
	    else
	      {
		int k = [unknowns indexOfObject: [data objectAtIndex: 5]];
		Tf = gsl_vector_get (vrs, k);
	      }
	    
	    // Build the equations
	    gsl_vector_set (func, nEqu, Pi*Vi/Ti - Pf*Vf/Tf);
	    
	    nEqu += 1;
	  }
	  break;
	}
    }
}

@end
