text:
<?php
# MantisConnect - A webservice interface to Mantis Bug Tracker
# Copyright (C) 2004-2011  Victor Boctor - vboctor@users.sourceforge.net
# This program is distributed under dual licensing.  These include
# GPL and a commercial licenses.  Victor Boctor reserves the right to
# change the license of future releases.
# See docs/ folder for more details

require_once( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'mc_core.php' );

/**
 * Check if an issue with the given id exists.
 *
 * @param string $p_username  The name of the user trying to access the issue.
 * @param string $p_password  The password of the user.
 * @param integer $p_issue_id  The id of the issue to check.
 * @return boolean  true if there is an issue with the given id, false otherwise.
 */
function mc_issue_exists( $p_username, $p_password, $p_issue_id ) {
	$t_user_id = mci_check_login( $p_username, $p_password );
	if( $t_user_id === false ) {
		return mci_soap_fault_login_failed();
	}

	if( !bug_exists( $p_issue_id ) ) {
		return false;
	}

	$t_project_id = bug_get_field( $p_issue_id, 'project_id' );
	if( !mci_has_readonly_access( $t_user_id, $t_project_id ) ) {

		// if we return an error here, then we answered the question!
		return false;
	}

	return true;
}

/**
 * Get all details about an issue.
 *
 * @param string $p_username  The name of the user trying to access the issue.
 * @param string $p_password  The password of the user.
 * @param integer $p_issue_id  The id of the issue to retrieve.
 * @return Array that represents an IssueData structure
 */
function mc_issue_get( $p_username, $p_password, $p_issue_id ) {
	$t_user_id = mci_check_login( $p_username, $p_password );
	if( $t_user_id === false ) {
		return mci_soap_fault_login_failed();
	}

	$t_lang = mci_get_user_lang( $t_user_id );

	if( !bug_exists( $p_issue_id ) ) {
		return new soap_fault( 'Client', '', 'Issue does not exist.' );
	}

	$t_project_id = bug_get_field( $p_issue_id, 'project_id' );
	if( !mci_has_readonly_access( $t_user_id, $t_project_id ) ) {
		return mci_soap_fault_access_denied( $t_user_id );
	}

	if( !access_has_bug_level( VIEWER, $p_issue_id, $t_user_id ) ){
	    return mci_soap_fault_access_denied( $t_user_id );
	}

	$t_bug = bug_get( $p_issue_id, true );
	$t_issue_data = array();

	$t_issue_data['id'] = $p_issue_id;
	$t_issue_data['view_state'] = mci_enum_get_array_by_id( $t_bug->view_state, 'view_state', $t_lang );
	$t_issue_data['last_updated'] = timestamp_to_iso8601( $t_bug->last_updated );

	$t_issue_data['project'] = mci_project_as_array_by_id( $t_bug->project_id );
	$t_issue_data['category'] = mci_get_category( $t_bug->category_id );
	$t_issue_data['priority'] = mci_enum_get_array_by_id( $t_bug->priority, 'priority', $t_lang );
	$t_issue_data['severity'] = mci_enum_get_array_by_id( $t_bug->severity, 'severity', $t_lang );
	$t_issue_data['status'] = mci_enum_get_array_by_id( $t_bug->status, 'status', $t_lang );

	$t_issue_data['reporter'] = mci_account_get_array_by_id( $t_bug->reporter_id );
	$t_issue_data['summary'] = $t_bug->summary;
	$t_issue_data['version'] = mci_null_if_empty( $t_bug->version );
	$t_issue_data['build'] = mci_null_if_empty( $t_bug->build );
	$t_issue_data['platform'] = mci_null_if_empty( $t_bug->platform );
	$t_issue_data['os'] = mci_null_if_empty( $t_bug->os );
	$t_issue_data['os_build'] = mci_null_if_empty( $t_bug->os_build );
	$t_issue_data['reproducibility'] = mci_enum_get_array_by_id( $t_bug->reproducibility, 'reproducibility', $t_lang );
	$t_issue_data['date_submitted'] = timestamp_to_iso8601( $t_bug->date_submitted );

	$t_issue_data['sponsorship_total'] = $t_bug->sponsorship_total;

	if( !empty( $t_bug->handler_id ) ) {
		$t_issue_data['handler'] = mci_account_get_array_by_id( $t_bug->handler_id );
	}

	$t_issue_data['projection'] = mci_enum_get_array_by_id( $t_bug->projection, 'projection', $t_lang );
	$t_issue_data['eta'] = mci_enum_get_array_by_id( $t_bug->eta, 'eta', $t_lang );

	$t_issue_data['resolution'] = mci_enum_get_array_by_id( $t_bug->resolution, 'resolution', $t_lang );
	$t_issue_data['fixed_in_version'] = mci_null_if_empty( $t_bug->fixed_in_version );
	$t_issue_data['target_version'] = mci_null_if_empty( $t_bug->target_version );
	$t_issue_data['due_date'] = mci_issue_get_due_date( $t_bug );

	$t_issue_data['description'] = $t_bug->description;
	$t_issue_data['steps_to_reproduce'] = mci_null_if_empty( $t_bug->steps_to_reproduce );
	$t_issue_data['additional_information'] = mci_null_if_empty( $t_bug->additional_information );

	$t_issue_data['attachments'] = mci_issue_get_attachments( $p_issue_id );
	$t_issue_data['relationships'] = mci_issue_get_relationships( $p_issue_id, $t_user_id );
	$t_issue_data['notes'] = mci_issue_get_notes( $p_issue_id );
	$t_issue_data['custom_fields'] = mci_issue_get_custom_fields( $p_issue_id );
	$t_issue_data['monitors'] = mci_account_get_array_by_ids( bug_get_monitors ( $p_issue_id ) );

	return $t_issue_data;
}

/**
 * Returns the category name, possibly null if no category is assigned
 *
 * @param int $p_category_id
 * @return string
 */
function mci_get_category( $p_category_id ) {
	if ( $p_category_id == 0 )
		return '';

	return mci_null_if_empty( category_get_name( $p_category_id ) );
}

/**
 *
 * @param BugData $bug
 * @return soapval the value to be encoded as the due date
 */
function mci_issue_get_due_date( $p_bug ) {
	if ( access_has_bug_level( config_get( 'due_date_view_threshold' ), $p_bug->id )  && !date_is_null( $p_bug->due_date ) ) {
		return new soapval( 'due_date', 'xsd:dateTime', timestamp_to_iso8601( $p_bug->due_date ) );
	} else {
		return new soapval( 'due_date','xsd:dateTime', null );
	}

}

/**
 * Sets the supplied array of custom field values to the specified issue id.
 *
 * @param $p_issue_id   Issue id to apply custom field values to.
 * @param $p_custom_fields  The array of custom field values as described in the webservice complex types.
 * @param boolean $p_log_insert create history logs for new values
 */
function mci_issue_set_custom_fields( $p_issue_id, &$p_custom_fields, $p_log_insert ) {
	# set custom field values on the submitted issue
	if( isset( $p_custom_fields ) && is_array( $p_custom_fields ) ) {
		foreach( $p_custom_fields as $t_custom_field ) {
			# get custom field id from object ref
			$t_custom_field_id = mci_get_custom_field_id_from_objectref( $t_custom_field['field'] );

			if( $t_custom_field_id == 0 ) {
				return new soap_fault( 'Client', '', 'Custom field ' . $t_custom_field['field']['name'] . ' not found.' );
			}

			# skip if current user doesn't have login access.
			if( !custom_field_has_write_access( $t_custom_field_id, $p_issue_id ) ) {
				continue;
			}

			$t_value = $t_custom_field['value'];

			if( !custom_field_validate( $t_custom_field_id, $t_value ) ) {
				return new soap_fault( 'Client', '', 'Invalid custom field value for field id ' . $t_custom_field_id . ' .');
			}

			if( !custom_field_set_value( $t_custom_field_id, $p_issue_id, $t_value, $p_log_insert  ) ) {
				return new soap_fault( 'Server', '', 'Unable to set custom field value for field id ' . $t_custom_field_id . ' to issue ' . $p_issue_id. ' .' );
			}
		}
	}
}

/**
 * Get the custom field values associated with the specified issue id.
 *
 * @param $p_issue_id   Issue id to get the custom field values for.
 *
 * @return null if no custom field defined for the project that contains the issue, or if no custom
 *              fields are accessible to the current user.
 */
function mci_issue_get_custom_fields( $p_issue_id ) {
	$t_project_id = bug_get_field( $p_issue_id, 'project_id' );

	$t_custom_fields = array();
	$t_related_custom_field_ids = custom_field_get_linked_ids( $t_project_id );

	foreach( $t_related_custom_field_ids as $t_id ) {
		$t_def = custom_field_get_definition( $t_id );

		if( custom_field_has_read_access( $t_id, $p_issue_id ) ) {

			# user has not access to read this custom field.
			$t_value = custom_field_get_value( $t_id, $p_issue_id );
			if( $t_value === false ) {
				continue;
			}

			# return a blank string if the custom field value is undefined
			if( $t_value === null ) {
				$t_value = '';
			}

			$t_custom_field_value = array();
			$t_custom_field_value['field'] = array();
			$t_custom_field_value['field']['id'] = $t_id;
			$t_custom_field_value['field']['name'] = $t_def['name'];
			$t_custom_field_value['value'] = $t_value;

			$t_custom_fields[] = $t_custom_field_value;
		}
	}

	# foreach

	return( count( $t_custom_fields ) == 0 ? null : $t_custom_fields );
}

/**
 * Get the attachments of an issue.
 *
 * @param integer $p_issue_id  The id of the issue to retrieve the attachments for
 * @return Array that represents an AttachmentData structure
 */
function mci_issue_get_attachments( $p_issue_id ) {
	$t_attachment_rows = bug_get_attachments( $p_issue_id );

	if ( $t_attachment_rows == null) {
		return array();
	}

	$t_result = array();
	foreach( $t_attachment_rows as $t_attachment_row ) {
		if ( !file_can_view_bug_attachments( $p_issue_id, (int)$t_attachment_row['user_id'] ) ) {
			continue;
		}
		$t_attachment = array();
		$t_attachment['id'] = $t_attachment_row['id'];
		$t_attachment['filename'] = $t_attachment_row['filename'];
		$t_attachment['size'] = $t_attachment_row['filesize'];
		$t_attachment['content_type'] = $t_attachment_row['file_type'];
		$t_attachment['date_submitted'] = timestamp_to_iso8601( $t_attachment_row['date_added'] );
		$t_attachment['download_url'] = mci_get_mantis_path() . 'file_download.php?file_id=' . $t_attachment_row['id'] . '&type=bug';
		$t_attachment['user_id'] = $t_attachment_row['user_id'];
		$t_result[] = $t_attachment;
	}

	return $t_result;
}

/**
 * Get the relationships of an issue.
 *
 * @param integer $p_issue_id  The id of the issue to retrieve the relationships for
 * @return Array that represents an RelationShipData structure
 */
function mci_issue_get_relationships( $p_issue_id, $p_user_id ) {
	$t_relationships = array();

	$t_src_relationships = relationship_get_all_src( $p_issue_id );
	foreach( $t_src_relationships as $t_relship_row ) {
		if( access_has_bug_level( config_get( 'mc_readonly_access_level_threshold' ), $t_relship_row->dest_bug_id, $p_user_id ) ) {
			$t_relationship = array();
			$t_reltype = array();
			$t_relationship['id'] = $t_relship_row->id;
			$t_reltype['id'] = $t_relship_row->type;
			$t_reltype['name'] = relationship_get_description_src_side( $t_relship_row->type );
			$t_relationship['type'] = $t_reltype;
			$t_relationship['target_id'] = $t_relship_row->dest_bug_id;
			$t_relationships[] = $t_relationship;
		}
	}

	$t_dest_relationships = relationship_get_all_dest( $p_issue_id );
	foreach( $t_dest_relationships as $t_relship_row ) {
		if( access_has_bug_level( config_get( 'mc_readonly_access_level_threshold' ), $t_relship_row->src_bug_id, $p_user_id ) ) {
			$t_relationship = array();
			$t_relationship['id'] = $t_relship_row->id;
			$t_reltype = array();
			$t_reltype['id'] = relationship_get_complementary_type( $t_relship_row->type );
			$t_reltype['name'] = relationship_get_description_dest_side( $t_relship_row->type );
			$t_relationship['type'] = $t_reltype;
			$t_relationship['target_id'] = $t_relship_row->src_bug_id;
			$t_relationships[] = $t_relationship;
		}
	}

	return (count( $t_relationships ) == 0 ? null : $t_relationships );
}

/**
 * Get all visible notes for a specific issue
 *
 * @param integer $p_issue_id  The id of the issue to retrieve the notes for
 * @return Array that represents an IssueNoteData structure
 */
function mci_issue_get_notes( $p_issue_id ) {
	$t_user_id = auth_get_current_user_id();
	$t_lang = mci_get_user_lang( $t_user_id );
	$t_project_id = bug_get_field( $p_issue_id, 'project_id' );
	$t_user_bugnote_order = 'ASC'; // always get the notes in ascending order for consistency to the calling application.
	$t_has_time_tracking_access = access_has_bug_level( config_get( 'time_tracking_view_threshold' ), $p_issue_id );

	$t_result = array();
	foreach( bugnote_get_all_visible_bugnotes( $p_issue_id, $t_user_bugnote_order, 0 ) as $t_value ) {
		$t_bugnote = array();
		$t_bugnote['id'] = $t_value->id;
		$t_bugnote['reporter'] = mci_account_get_array_by_id( $t_value->reporter_id );
		$t_bugnote['date_submitted'] = timestamp_to_iso8601( $t_value->date_submitted );
		$t_bugnote['last_modified'] = timestamp_to_iso8601( $t_value->last_modified );
		$t_bugnote['text'] = $t_value->note;
		$t_bugnote['view_state'] = mci_enum_get_array_by_id( $t_value->view_state, 'view_state', $t_lang );
		$t_bugnote['time_tracking'] = $t_has_time_tracking_access ? $t_value->time_tracking : 0;
		$t_bugnote['note_type'] = $t_value->note_type;
		$t_bugnote['note_attr'] = $t_value->note_attr;

		$t_result[] = $t_bugnote;
	}

	return (count( $t_result ) == 0 ? null : $t_result );
}

/**
 * Sets the monitors of the specified issue
 *
 *
This functions performs access level checks and only performs operations which would
 * modify the existing monitors list.

 *
 * @param int $p_issue_id the issue id to set the monitors for
 * @param int $p_user_id the user which requests the monitor change
 * @param array $p_monitors An array of arrays with the id field set to the id
 *  of the users which should monitor this issue.
 */
function mci_issue_set_monitors( $p_issue_id , $p_user_id, $p_monitors ) {

    $t_existing_monitors = bug_get_monitors( $p_issue_id );

    $t_monitors = array();
    foreach ( $p_monitors as $t_monitor )
        $t_monitors[] = $t_monitor['id'];

    foreach ( $t_monitors as $t_user_id ) {

    	if ( $p_user_id == $t_user_id ) {
    		if ( ! access_has_bug_level( config_get( 'monitor_bug_threshold' ), $p_issue_id ) )
    		    continue;
	    } else {
	    	if ( !access_has_bug_level( config_get( 'monitor_add_others_bug_threshold' ), $p_issue_id ) )
	    	    continue;
	    }

       if ( in_array( $p_user_id, $t_existing_monitors) )
           continue;

        bug_monitor( $p_issue_id, $t_user_id);
    }

    foreach ( $t_existing_monitors as $t_user_id ) {

    	if ( $p_user_id == $t_user_id ) {
    		if ( ! access_has_bug_level( config_get( 'monitor_bug_threshold' ), $p_issue_id ) )
    		    continue;
	    } else {
	    	if ( !access_has_bug_level( config_get( 'monitor_delete_others_bug_threshold' ), $p_issue_id ) )
	    	    continue;
	    }

        if ( in_array( $p_user_id, $t_monitors) )
            continue;

        bug_unmonitor( $p_issue_id, $t_user_id);
    }

}

/**
 * Get the biggest issue id currently used.
 *
 * @param string $p_username  The name of the user trying to retrieve the information
 * @param string $p_password  The password of the user.
 * @param int    $p_project_id	-1 default project, 0 for all projects, otherwise project id.
 * @return integer  The biggest used issue id.
 */
function mc_issue_get_biggest_id( $p_username, $p_password, $p_project_id ) {
	$t_user_id = mci_check_login( $p_username, $p_password );
	if( $t_user_id === false ) {
		return mci_soap_fault_login_failed();
	}

	$t_any = defined( 'META_FILTER_ANY' ) ? META_FILTER_ANY : 'any';
	$t_none = defined( 'META_FILTER_NONE' ) ? META_FILTER_NONE : 'none';

	$t_filter = array(
		'show_category' => Array(
			'0' => $t_any,
		),
		'show_severity' => Array(
			'0' => $t_any,
		),
		'show_status' => Array(
			'0' => $t_any,
		),
		'highlight_changed' => 0,
		'reporter_id' => Array(
			'0' => $t_any,
		),
		'handler_id' => Array(
			'0' => $t_any,
		),
		'show_resolution' => Array(
			'0' => $t_any,
		),
		'show_build' => Array(
			'0' => $t_any,
		),
		'show_version' => Array(
			'0' => $t_any,
		),
		'hide_status' => Array(
			'0' => $t_none,
		),
		'user_monitor' => Array(
			'0' => $t_any,
		),
		'dir' => 'DESC',
		'sort' => 'id',
	);

	$t_page_number = 1;
	$t_per_page = 1;
	$t_bug_count = 0;
	$t_page_count = 0;

	# Get project id, if -1, then retrieve the current which will be the default since there is no cookie.
	$t_project_id = $p_project_id;
	if( $t_project_id == -1 ) {
		$t_project_id = helper_get_current_project();
	}

	if(( $t_project_id > 0 ) && !project_exists( $t_project_id ) ) {
		return new soap_fault( 'Client', '', "Project '$t_project_id' does not exist." );
	}

	if( !mci_has_readonly_access( $t_user_id, $t_project_id ) ) {
		return mci_soap_fault_access_denied( $t_user_id );
	}

	$t_rows = filter_get_bug_rows( $t_page_number, $t_per_page, $t_page_count, $t_bug_count, $t_filter, $t_project_id, $t_user_id );
	if( count( $t_rows ) == 0 ) {
		return 0;
	} else {
		return $t_rows[0]->id;
	}
}

/**
 * Get the id of an issue via the issue's summary.
 *
 * @param string $p_username  The name of the user trying to delete the issue.
 * @param string $p_password  The password of the user.
 * @param string $p_summary  The summary of the issue to retrieve.
 * @return integer  The id of the issue with the given summary, 0 if there is no such issue.
 */
function mc_issue_get_id_from_summary( $p_username, $p_password, $p_summary ) {
	$t_user_id = mci_check_login( $p_username, $p_password );
	if( $t_user_id === false ) {
		return mci_soap_fault_login_failed();
	}

	$t_bug_table = db_get_table( 'mantis_bug_table' );

	$query = "SELECT id
		FROM $t_bug_table
		WHERE summary = " . db_param();

	$result = db_query_bound( $query, Array( $p_summary ), 1 );

	if( db_num_rows( $result ) == 0 ) {
		return 0;
	} else {
		while(( $row = db_fetch_array( $result ) ) !== false ) {
			$t_issue_id = (int) $row['id'];
			$t_project_id = bug_get_field( $t_issue_id, 'project_id' );

			if( mci_has_readonly_access( $t_user_id, $t_project_id ) ) {
				return $t_issue_id;
			}
		}

		// no issue found that belongs to a project that the user has read access to.
		return 0;
	}
}

/**
 * Add an issue to the database.
 *
 * @param string $p_username  The name of the user trying to add the issue.
 * @param string $p_password  The password of the user.
 * @param Array $p_issue  A IssueData structure containing information about the new issue.
 * @return integer  The id of the created issue.
 */
function mc_issue_add( $p_username, $p_password, $p_issue ) {
	$t_user_id = mci_check_login( $p_username, $p_password );
	if( $t_user_id === false ) {
		return mci_soap_fault_login_failed();
	}

	$t_project = $p_issue['project'];

	$t_project_id = mci_get_project_id( $t_project );

	if( !mci_has_readwrite_access( $t_user_id, $t_project_id ) ) {
		return mci_soap_fault_access_denied( $t_user_id );
	}

	$t_handler_id = isset( $p_issue['handler'] ) ? mci_get_user_id( $p_issue['handler'] ) : 0;
	$t_priority_id = isset( $p_issue['priority'] ) ? mci_get_priority_id( $p_issue['priority'] ) : config_get( 'default_bug_priority' );
	$t_severity_id = isset( $p_issue['severity'] ) ?  mci_get_severity_id( $p_issue['severity'] ) : config_get( 'default_bug_severity' );
	$t_status_id = isset( $p_issue['status'] ) ? mci_get_status_id( $p_issue['status'] ) : config_get( 'bug_submit_status' );
	$t_reproducibility_id = isset( $p_issue['reproducibility'] ) ?  mci_get_reproducibility_id( $p_issue['reproducibility'] ) : config_get( 'default_bug_reproducibility' );
	$t_resolution_id =  isset( $p_issue['resolution'] ) ? mci_get_resolution_id( $p_issue['resolution'] ) : config_get('default_bug_resolution');
	$t_projection_id = isset( $p_issue['projection'] ) ? mci_get_projection_id( $p_issue['projection'] ) : config_get('default_bug_resolution');
	$t_eta_id = isset( $p_issue['eta'] ) ? mci_get_eta_id( $p_issue['eta'] ) : config_get('default_bug_eta');
	$t_view_state_id = isset( $p_issue['view_state'] ) ?  mci_get_view_state_id( $p_issue['view_state'] ) : config_get( 'default_bug_view_status' );
	$t_reporter_id = isset( $p_issue['reporter'] ) ? mci_get_user_id( $p_issue['reporter'] )  : 0;
	$t_summary = $p_issue['summary'];
	$t_description = $p_issue['description'];
	$t_notes = isset( $p_issue['notes'] ) ? $p_issue['notes'] : array();

	if( $t_reporter_id == 0 ) {
		$t_reporter_id = $t_user_id;
	} else {
		if( $t_reporter_id != $t_user_id ) {

			# Make sure that active user has access level required to specify a different reporter.
			$t_specify_reporter_access_level = config_get( 'mc_specify_reporter_on_add_access_level_threshold' );
			if( !access_has_project_level( $t_specify_reporter_access_level, $t_project_id, $t_user_id ) ) {
				return mci_soap_fault_access_denied( $t_user_id, "Active user does not have access level required to specify a different issue reporter" );
			}
		}
	}

	if(( $t_project_id == 0 ) || !project_exists( $t_project_id ) ) {
		if( $t_project_id == 0 ) {
			return new soap_fault( 'Client', '', "Project '" . $t_project['name'] . "' does not exist." );
		} else {
			return new soap_fault( 'Client', '', "Project with id '" . $t_project_id . "' does not exist." );
		}
	}

	if( !access_has_project_level( config_get( 'report_bug_threshold' ), $t_project_id, $t_user_id ) ) {
		return mci_soap_fault_access_denied( "User '$t_user_id' does not have access right to report issues" );
	}

	#if ( !access_has_project_level( config_get( 'report_bug_threshold' ), $t_project_id ) ||
	#	!access_has_project_level( config_get( 'report_bug_threshold' ), $t_project_id, $v_reporter ) ) {
	#	return new soap_fault( 'Client', '', "User does not have access right to report issues." );
	#}

	if(( $t_handler_id != 0 ) && !user_exists( $t_handler_id ) ) {
		return new soap_fault( 'Client', '', "User '$t_handler_id' does not exist." );
	}

	$t_category = isset ( $p_issue['category'] ) ? $p_issue['category'] : null;

	$t_category_id = translate_category_name_to_id( $t_category, $t_project_id );
	if ( $t_category_id == 0 && !config_get( 'allow_no_category' ) ) {
		if ( !isset( $p_issue['category'] ) || is_blank( $p_issue['category'] ) ) {
			return new soap_fault( 'Client', '', "Category field must be supplied." );
		} else {
			return new soap_fault( 'Client', '', "Category '" . $p_issue['category'] . "' not found for project '$t_project_id'." );
		}
	}

	if ( isset( $p_issue['version'] ) && !is_blank( $p_issue['version'] ) && !version_get_id( $p_issue['version'], $t_project_id ) ) {
		$t_version = $p_issue['version'];

		$t_error_when_version_not_found = config_get( 'mc_error_when_version_not_found' );
		if( $t_error_when_version_not_found == ON ) {
			$t_project_name = project_get_name( $t_project_id );
			return new soap_fault( 'Client', '', "Version '$t_version' does not exist in project '$t_project_name'." );
		} else {
			$t_version_when_not_found = config_get( 'mc_version_when_not_found' );
			$t_version = $t_version_when_not_found;
		}
	}

	if ( is_blank( $t_summary ) ) {
		return new soap_fault( 'Client', '', "Mandatory field 'summary' is missing." );
	}

	if ( is_blank( $t_description ) ) {
		return new soap_fault( 'Client', '', "Mandatory field 'description' is missing." );
	}

	$t_bug_data = new BugData;
	$t_bug_data->profile_id = 0;
	$t_bug_data->project_id = $t_project_id;
	$t_bug_data->reporter_id = $t_reporter_id;
	$t_bug_data->handler_id = $t_handler_id;
	$t_bug_data->priority = $t_priority_id;
	$t_bug_data->severity = $t_severity_id;
	$t_bug_data->reproducibility = $t_reproducibility_id;
	$t_bug_data->status = $t_status_id;
	$t_bug_data->resolution = $t_resolution_id;
	$t_bug_data->projection = $t_projection_id;
	$t_bug_data->category_id = $t_category_id;
	$t_bug_data->date_submitted = isset( $p_issue['date_submitted'] ) ? $p_issue['date_submitted'] : '';
	$t_bug_data->last_updated = isset( $p_issue['last_updated'] ) ? $p_issue['last_updated'] : '';
	$t_bug_data->eta = $t_eta_id;
	$t_bug_data->os = isset( $p_issue['os'] ) ? $p_issue['os'] : '';
	$t_bug_data->os_build = isset( $p_issue['os_build'] ) ? $p_issue['os_build'] : '';
	$t_bug_data->platform = isset( $p_issue['platform'] ) ? $p_issue['platform'] : '';
	$t_bug_data->version = isset( $p_issue['version'] ) ? $p_issue['version'] : '';
	$t_bug_data->fixed_in_version = isset( $p_issue['fixed_in_version'] ) ? $p_issue['fixed_in_version'] : '';
	$t_bug_data->build = isset( $p_issue['build'] ) ? $p_issue['build'] : '';
	$t_bug_data->view_state = $t_view_state_id;
	$t_bug_data->summary = $t_summary;
	$t_bug_data->sponsorship_total = isset( $p_issue['sponsorship_total'] ) ? $p_issue['sponsorship_total'] : 0;

	if ( isset( $p_issue['due_date'] ) && access_has_global_level( config_get( 'due_date_update_threshold' ) ) ) {
		$t_bug_data->due_date = mci_iso8601_to_timestamp( $p_issue['due_date'] );
	} else {
		$t_bug_data->due_date = date_get_null();
	}

	if( access_has_project_level( config_get( 'roadmap_update_threshold' ), $t_bug_data->project_id, $t_user_id ) ) {
		$t_bug_data->target_version = isset( $p_issue['target_version'] ) ? $p_issue['target_version'] : '';
	}

	# omitted:
	# var $bug_text_id
	# $t_bug_data->profile_id;
	# extended info
	$t_bug_data->description = $t_description;
	$t_bug_data->steps_to_reproduce = isset( $p_issue['steps_to_reproduce'] ) ? $p_issue['steps_to_reproduce'] : '';
	$t_bug_data->additional_information = isset( $p_issue['additional_information'] ) ? $p_issue['additional_information'] : '';

	# submit the issue
	$t_issue_id = $t_bug_data->create();

	mci_issue_set_custom_fields( $t_issue_id, $p_issue['custom_fields'], false );
	if ( isset ( $p_issue['monitors'] ) )
	    mci_issue_set_monitors( $t_issue_id , $t_user_id, $p_issue['monitors'] );

	if( isset( $t_notes ) && is_array( $t_notes ) ) {
		foreach( $t_notes as $t_note ) {
			if( isset( $t_note['view_state'] ) ) {
				$t_view_state = $t_note['view_state'];
			} else {
				$t_view_state = config_get( 'default_bugnote_view_status' );
			}

			$note_type = isset ( $t_note['note_type'] ) ? (int) $t_note['note_type'] : BUGNOTE;
			$note_attr = isset ( $t_note['note_type'] ) ? $t_note['note_attr'] : '';

			$t_view_state_id = mci_get_enum_id_from_objectref( 'view_state', $t_view_state );
			bugnote_add( $t_issue_id, $t_note['text'], mci_get_time_tracking_from_note( $t_issue_id, $t_note ), $t_view_state_id == VS_PRIVATE, $note_type, $note_attr, $t_user_id, FALSE );
		}
	}

	email_new_bug( $t_issue_id );

	return $t_issue_id;
}

/**
 * Update Issue in database
 *
 * Created By KGB
 * @param string $p_username The name of the user trying to add the issue.
 * @param string $p_password The password of the user.
 * @param Array $p_issue A IssueData structure containing information about the new issue.
 * @return integer The id of the created issue.
 */
function mc_issue_update( $p_username, $p_password, $p_issue_id, $p_issue ) {
	$t_user_id = mci_check_login( $p_username, $p_password );
	if( $t_user_id === false ) {
		return mci_soap_fault_login_failed();
	}

	if( !bug_exists( $p_issue_id ) ) {
		return new soap_fault( 'Client', '', "Issue '$p_issue_id' does not exist." );
	}

	$t_project_id = bug_get_field( $p_issue_id, 'project_id' );

	if( !mci_has_readwrite_access( $t_user_id, $t_project_id ) ) {
		return mci_soap_fault_access_denied( $t_user_id );
	}

	$t_project_id = mci_get_project_id( $p_issue['project'] );
	$t_reporter_id = isset( $p_issue['reporter'] ) ? mci_get_user_id( $p_issue['reporter'] )  : $t_user_id ;
	$t_handler_id = isset( $p_issue['handler'] ) ? mci_get_user_id( $p_issue['handler'] ) : 0;
	$t_project = $p_issue['project'];
	$t_summary = isset( $p_issue['summary'] ) ? $p_issue['summary'] : '';
	$t_description = isset( $p_issue['description'] ) ? $p_issue['description'] : '';

	if(( $t_project_id == 0 ) || !project_exists( $t_project_id ) ) {
		if( $t_project_id == 0 ) {
			return new soap_fault( 'Client', '', "Project '" . $t_project['name'] . "' does not exist." );
		}
		return new soap_fault( 'Client', '', "Project '$t_project_id' does not exist." );
	}

	if( !access_has_bug_level( config_get( 'update_bug_threshold' ), $p_issue_id, $t_user_id ) ) {
		return mci_soap_fault_access_denied( $t_user_id,  "Not enough rights to update issues" );
	}

	if(( $t_handler_id != 0 ) && !user_exists( $t_handler_id ) ) {
		return new soap_fault( 'Client', '', "User '$t_handler_id' does not exist." );
	}

	$t_category = isset ( $p_issue['category'] ) ? $p_issue['category'] : null;

	$t_category_id = translate_category_name_to_id( $t_category, $t_project_id );
	if ( $t_category_id == 0 && !config_get( 'allow_no_category' ) ) {
		if ( isset( $p_issue['category'] ) && !is_blank( $p_issue['category'] ) ) {
			return new soap_fault( 'Client', '', "Category field must be supplied." );
		} else {
			return new soap_fault( 'Client', '', "Category '" . $p_issue['category'] . "' not found for project '$t_project_name'." );
		}
	}

	if ( isset( $p_issue['version'] ) && !is_blank( $p_issue['version'] ) && !version_get_id( $p_issue['version'], $t_project_id ) ) {
		$t_error_when_version_not_found = config_get( 'mc_error_when_version_not_found' );
		if( $t_error_when_version_not_found == ON ) {
			$t_project_name = project_get_name( $t_project_id );
			return new soap_fault( 'Client', '', "Version '" . $p_issue['version'] . "' does not exist in project '$t_project_name'." );
		} else {
			$t_version_when_not_found = config_get( 'mc_version_when_not_found' );
			$p_issue['version'] = $t_version_when_not_found;
		}
	}

	if ( is_blank( $t_summary ) ) {
		return new soap_fault( 'Client', '', "Mandatory field 'summary' is missing." );
	}

	if ( is_blank( $t_description ) ) {
		return new soap_fault( 'Client', '', "Mandatory field 'description' is missing." );
	}

	// fields which we expect to always be set
	$t_bug_data = bug_get( $p_issue_id, true );
	$t_bug_data->project_id = $t_project_id;
	$t_bug_data->reporter_id = $t_reporter_id;
	$t_bug_data->handler_id = $t_handler_id;
	$t_bug_data->category_id = $t_category_id;
	$t_bug_data->summary = $t_summary;
	$t_bug_data->description = $t_description;

	// fields which might not be set
	if ( isset ( $p_issue['steps_to_reproduce'] ) )
		$t_bug_data->steps_to_reproduce = $p_issue['steps_to_reproduce'];
	if ( isset ( $p_issue['additional_information'] ) )
		$t_bug_data->additional_information = $p_issue['additional_information'];
	if ( isset( $p_issue['priority'] ) )
		$t_bug_data->priority = mci_get_priority_id( $p_issue['priority'] );
	if ( isset( $p_issue['severity'] ) )
		$t_bug_data->severity = mci_get_severity_id( $p_issue['severity'] );
	if ( isset( $p_issue['status'] ) )
		$t_bug_data->status = mci_get_status_id ( $p_issue['status'] );
	if ( isset ( $p_issue['reproducibility'] ) )
		$t_bug_data->reproducibility = mci_get_reproducibility_id( $p_issue['reproducibility'] );
	if ( isset ( $p_issue['resolution'] ) )
		$t_bug_data->resolution = mci_get_resolution_id( $p_issue['resolution'] );
	if ( isset ( $p_issue['projection'] ) )
		$t_bug_data->projection = mci_get_projection_id( $p_issue['projection'] );
	if ( isset ( $p_issue['eta'] ) )
		$t_bug_data->eta = mci_get_eta_id( $p_issue['eta'] );
	if ( isset ( $p_issue['view_state'] ) )
		$t_bug_data->view_state = mci_get_view_state_id( $p_issue['view_state'] );
	if ( isset ( $p_issue['date_submitted'] ) )
		$t_bug_data->date_submitted = $p_issue['date_submitted'];
	if ( isset ( $p_issue['date_updated'] ) )
		$t_bug_data->last_updated = $p_issue['last_updated'];
	if ( isset ( $p_issue['os'] ) )
		$t_bug_data->os = $p_issue['os'];
	if ( isset ( $p_issue['os_build'] ) )
		$t_bug_data->os_build = $p_issue['os_build'];
	if ( isset ( $p_issue['build'] ) )
		$t_bug_data->build = $p_issue['build'];
	if ( isset ( $p_issue['platform'] ) )
		$t_bug_data->platform = $p_issue['platform'];
	if ( isset ( $p_issue['version'] ) )
		$t_bug_data->version = $p_issue['version'];
	if ( isset ( $p_issue['fixed_in_version'] ) )
		$t_bug_data->fixed_in_version = $p_issue['fixed_in_version'];

	if ( isset( $p_issue['due_date'] ) && access_has_global_level( config_get( 'due_date_update_threshold' ) ) ) {
		$t_bug_data->due_date = mci_iso8601_to_timestamp( $p_issue['due_date'] );
	} else {
		$t_bug_data->due_date = date_get_null();
	}

	if( access_has_project_level( config_get( 'roadmap_update_threshold' ), $t_bug_data->project_id, $t_user_id ) ) {
		$t_bug_data->target_version = isset( $p_issue['target_version'] ) ? $p_issue['target_version'] : '';
	}

	# submit the issue
	$t_is_success = $t_bug_data->update( /* update_extended */ true, /* bypass_email */ true );

	mci_issue_set_custom_fields( $p_issue_id, $p_issue['custom_fields'], true );
	if ( isset ( $p_issue['monitors'] ) )
	    mci_issue_set_monitors( $p_issue_id , $t_user_id, $p_issue['monitors'] );

	if ( isset( $p_issue['notes'] ) && is_array( $p_issue['notes'] ) ) {
		foreach ( $p_issue['notes'] as $t_note ) {
			if ( isset( $t_note['view_state'] ) ) {
				$t_view_state = $t_note['view_state'];
			} else {
				$t_view_state = config_get( 'default_bugnote_view_status' );
			}

			if ( isset( $t_note['id'] ) && ( (int)$t_note['id'] > 0 ) ) {
				$t_bugnote_id = (integer)$t_note['id'];

				$t_view_state_id = mci_get_enum_id_from_objectref( 'view_state', $t_view_state );

				if ( bugnote_exists( $t_bugnote_id ) ) {
					bugnote_set_text( $t_bugnote_id, $t_note['text'] );
					bugnote_set_view_state( $t_bugnote_id, $t_view_state_id == VS_PRIVATE );
	$t_eta_id = isset( $p_issue['eta'] ) ? mci_get_eta_id( $p_issue['eta'] ) : config_get('default_bug_eta');
					bugnote_date_update( $t_bugnote_id );
					if ( isset( $t_note['time_tracking'] ) )
						bugnote_set_time_tracking( $t_bugnote_id, mci_get_time_tracking_from_note( $p_issue_id, $t_note ) );
				}
			} else {
				$t_view_state_id = mci_get_enum_id_from_objectref( 'view_state', $t_view_state );

				$note_type = isset ( $t_note['note_type'] ) ? (int) $t_note['note_type'] : BUGNOTE;
			    $note_attr = isset ( $t_note['note_type'] ) ? $t_note['note_attr'] : '';

				bugnote_add( $p_issue_id, $t_note['text'], mci_get_time_tracking_from_note( $p_issue_id, $t_note ), $t_view_state_id == VS_PRIVATE, $note_type, $note_attr, $t_user_id, FALSE );
			}
		}
	}

	return $t_is_success;
}

/**
 * Delete the specified issue.
 *
 * @param string $p_username  The name of the user trying to delete the issue.
 * @param string $p_password  The password of the user.
 * @param integer $p_issue_id  The id of the issue to delete.
 * @return boolean  True if the issue has been deleted successfully, false otherwise.
 */
function mc_issue_delete( $p_username, $p_password, $p_issue_id ) {
	$t_user_id = mci_check_login( $p_username, $p_password );
	if( $t_user_id === false ) {
		return mci_soap_fault_login_failed();
	}

	if( !bug_exists( $p_issue_id ) ) {
		return new soap_fault( 'Client', '', "Issue '$p_issue_id' does not exist.");
	}

	$t_project_id = bug_get_field( $p_issue_id, 'project_id' );
	if( !mci_has_readwrite_access( $t_user_id, $t_project_id ) ) {
		return mci_soap_fault_access_denied( $t_user_id );
	}

	return bug_delete( $p_issue_id );
}

/**
 * Add a note to an existing issue.
 *
 * @param string $p_username  The name of the user trying to add a note to an issue.
 * @param string $p_password  The password of the user.
 * @param integer $p_issue_id  The id of the issue to add the note to.
 * @param IssueNoteData $p_note  The note to add.
 * @return integer The id of the added note.
 */
function mc_issue_note_add( $p_username, $p_password, $p_issue_id, $p_note ) {
	$t_user_id = mci_check_login( $p_username, $p_password );
	if( $t_user_id === false ) {
		return mci_soap_fault_login_failed();
	}

	if( (integer) $p_issue_id < 1 ) {
		return new soap_fault( 'Client', '', "Invalid issue id '$p_issue_id'" );
	}

	if( !bug_exists( $p_issue_id ) ) {
		return new soap_fault( 'Client', '', "Issue '$p_issue_id' does not exist." );
	}

	if ( !isset( $p_note['text'] ) || is_blank( $p_note['text'] ) ) {
		return new soap_fault( 'Client', '', "Issue note text must not be blank." );
	}

	$t_project_id = bug_get_field( $p_issue_id, 'project_id' );
	if( !mci_has_readwrite_access( $t_user_id, $t_project_id ) ) {
		return mci_soap_fault_access_denied( $t_user_id );
	}

	if( !access_has_bug_level( config_get( 'add_bugnote_threshold' ), $p_issue_id, $t_user_id ) ) {
		return mci_soap_fault_access_denied( $t_user_id, "You do not have access rights to add notes to this issue" );
	}

	if( bug_is_readonly( $p_issue_id ) ) {
		return mci_soap_fault_access_denied( $t_user_id, "Issue '$p_issue_id' is readonly" );
	}

	if( isset( $p_note['view_state'] ) ) {
		$t_view_state = $p_note['view_state'];
	} else {
		$t_view_state = array(
			'id' => config_get( 'default_bug_view_status' ),
		);
	}

	$t_view_state_id = mci_get_enum_id_from_objectref( 'view_state', $t_view_state );

	$note_type = isset ( $p_note['note_type'] ) ? (int) $p_note['note_type'] : BUGNOTE;
	$note_attr = isset ( $p_note['note_type'] ) ? $p_note['note_attr'] : '';

	return bugnote_add( $p_issue_id, $p_note['text'], mci_get_time_tracking_from_note( $p_issue_id, $p_note ), $t_view_state_id == VS_PRIVATE, $note_type, $note_attr, $t_user_id );
}

/**
 * Delete a note given its id.
 *
 * @param string $p_username  The name of the user trying to add a note to an issue.
 * @param string $p_password  The password of the user.
 * @param integer $p_issue_note_id  The id of the note to be deleted.
 * @return true: success, false: failure
 */
function mc_issue_note_delete( $p_username, $p_password, $p_issue_note_id ) {
	$t_user_id = mci_check_login( $p_username, $p_password );
	if( $t_user_id === false ) {
		return mci_soap_fault_login_failed();
	}

	if( (integer) $p_issue_note_id < 1 ) {
		return new soap_fault( 'Client', '', "Invalid issue note id '$p_issue_note_id'.");
	}

	if( !bugnote_exists( $p_issue_note_id ) ) {
		return new soap_fault( 'Client', '', "Issue note '$p_issue_note_id' does not exist.");
	}

	$t_issue_id = bugnote_get_field( $p_issue_note_id, 'bug_id' );
	$t_project_id = bug_get_field( $t_issue_id, 'project_id' );
	if( !mci_has_readwrite_access( $t_user_id, $t_project_id ) ) {
		return mci_soap_fault_access_denied( $t_user_id );
	}

	return bugnote_delete( $p_issue_note_id );
}

/**
 * Update a note
 *
 * @param string $p_username  The name of the user trying to add a note to an issue.
 * param string $p_password  The password of the user.
 * @param IssueNoteData $p_note  The note to update.
 * @return true on success, false on failure
 */
function mc_issue_note_update( $p_username, $p_password, $p_note ) {
    $t_user_id = mci_check_login( $p_username, $p_password );

    if( $t_user_id === false ) {
        return mci_soap_fault_login_failed();
    }

    if ( !isset( $p_note['id'] ) || is_blank( $p_note['id'] ) ) {
        return new soap_fault( 'Client', '', "Issue id must not be blank." );
    }

    if ( !isset( $p_note['text'] ) || is_blank( $p_note['text'] ) ) {
        return new soap_fault( 'Client', '', "Issue note text must not be blank." );
    }

    $t_issue_note_id = $p_note['id'];

    if( !bugnote_exists( $t_issue_note_id ) ) {
        return new soap_fault( 'Server', '', "Issue note '$t_issue_note_id' does not exist." );
    }

	$t_issue_id = bugnote_get_field( $t_issue_note_id, 'bug_id' );

	$t_project_id = bug_get_field( $t_issue_id, 'project_id' );

    if( !mci_has_readwrite_access( $t_user_id, $t_project_id ) ) {
        return mci_soap_fault_access_denied( $t_user_id );
    }

    if( !access_has_bug_level( config_get( 'add_bugnote_threshold' ), $t_issue_id, $t_user_id ) ) {
        return mci_soap_fault_access_denied( $t_user_id, "You do not have access rights to add notes to this issue" );
    }

    if( bug_is_readonly( $t_issue_id ) ) {
        return mci_soap_fault_access_denied( $t_user_id, "Issue ' . $t_issue_id . ' is readonly" );
    }

    if( isset( $p_note['view_state'] )) {
        $t_view_state = $p_note['view_state'];
        $t_view_state_id = mci_get_enum_id_from_objectref( 'view_state', $t_view_state );
        bugnote_set_view_state( $t_issue_note_id, $t_view_state_id );
    }

    bugnote_set_text( $t_issue_note_id, $p_note['text'] );

    return bugnote_date_update( $t_issue_note_id );
}

/**
 * Submit a new relationship.
 *
 * @param string $p_username  The name of the user trying to add a note to an issue.
 * @param string $p_password  The password of the user.
 * @param integer $p_issue_id  The id of the issue of the source issue.
 * @param RelationshipData $p_relationship  The relationship to add.
 * @return integer The id of the added relationship.
 */
function mc_issue_relationship_add( $p_username, $p_password, $p_issue_id, $p_relationship ) {
	$t_user_id = mci_check_login( $p_username, $p_password );
	$t_dest_issue_id = $p_relationship['target_id'];
	$t_rel_type = $p_relationship['type'];

	if( $t_user_id === false ) {
		return mci_soap_fault_login_failed();
	}

	$t_project_id = bug_get_field( $p_issue_id, 'project_id' );
	if( !mci_has_readwrite_access( $t_user_id, $t_project_id ) ) {
		return mci_soap_fault_access_denied( $t_user_id );
	}

	# user has access to update the bug...
	if( !access_has_bug_level( config_get( 'update_bug_threshold' ), $p_issue_id, $t_user_id ) ) {
		return mci_soap_fault_access_denied( $t_user_id, "Active user does not have access level required to add a relationship to this issue" );
	}

	# source and destination bugs are the same bug...
	if( $p_issue_id == $t_dest_issue_id ) {
		return new soap_fault( 'Client', '', "An issue can't be related to itself." );
	}

	# the related bug exists...
	if( !bug_exists( $t_dest_issue_id ) ) {
		return new soap_fault( 'Client', '', "Issue '$t_dest_issue_id' not found." );
	}

	# bug is not read-only...
	if( bug_is_readonly( $p_issue_id ) ) {
		return new mci_soap_fault_access_denied( $t_user_id, "Issue '$p_issue_id' is readonly" );
	}

	# user can access to the related bug at least as viewer...
	if( !access_has_bug_level( VIEWER, $t_dest_issue_id, $t_user_id ) ) {
		return mci_soap_fault_access_denied( $t_user_id, "The issue '$t_dest_issue_id' requires higher access level" );
	}

	$t_old_id_relationship = relationship_same_type_exists( $p_issue_id, $t_dest_issue_id, $t_rel_type['id'] );

	if( $t_old_id_relationship == 0 ) {
		relationship_add( $p_issue_id, $t_dest_issue_id, $t_rel_type['id'] );

		// The above function call into MantisBT does not seem to return a valid BugRelationshipData object.
		// So we call db_insert_id in order to find the id of the created relationship.
		$t_relationship_id = db_insert_id( db_get_table( 'mantis_bug_relationship_table' ) );

		# Add log line to the history (both bugs)
		history_log_event_special( $p_issue_id, BUG_ADD_RELATIONSHIP, $t_rel_type['id'], $t_dest_issue_id );
		history_log_event_special( $t_dest_issue_id, BUG_ADD_RELATIONSHIP, relationship_get_complementary_type( $t_rel_type['id'] ), $p_issue_id );

		# update bug last updated for both bugs
		bug_update_date( $p_issue_id );
		bug_update_date( $t_dest_issue_id );

		# send email notification to the users addressed by both the bugs
		email_relationship_added( $p_issue_id, $t_dest_issue_id, $t_rel_type['id'] );
		email_relationship_added( $t_dest_issue_id, $p_issue_id, relationship_get_complementary_type( $t_rel_type['id'] ) );

		return $t_relationship_id;
	} else {
		return new soap_fault( 'Client', '', "Relationship already exists." );
	}
}

/**
 * Delete the relationship with the specified target id.
 *
 * @param string $p_username  The name of the user trying to add a note to an issue.
 * @param string $p_password  The password of the user.
 * @param integer $p_issue_id  The id of the source issue for the relationship
 * @param integer $p_relationship_id  The id of relationship to delete.
 * @return true: success, false: failure
 */
function mc_issue_relationship_delete( $p_username, $p_password, $p_issue_id, $p_relationship_id ) {
	$t_user_id = mci_check_login( $p_username, $p_password );

	if( $t_user_id === false ) {
		return mci_soap_fault_login_failed();
	}

	$t_project_id = bug_get_field( $p_issue_id, 'project_id' );
	if( !mci_has_readwrite_access( $t_user_id, $t_project_id ) ) {
		return mci_soap_fault_access_denied( $t_user_id );
	}

	# user has access to update the bug...
	if( !access_has_bug_level( config_get( 'update_bug_threshold' ), $p_issue_id, $t_user_id ) ) {
		return mci_soap_fault_access_denied( $t_user_id , "Active user does not have access level required to remove a relationship from this issue." );
	}

	# bug is not read-only...
	if( bug_is_readonly( $p_issue_id ) ) {
		return mci_soap_fault_access_denied( $t_user_id , "Issue '$p_issue_id' is readonly." );
	}

	# retrieve the destination bug of the relationship
	$t_dest_issue_id = relationship_get_linked_bug_id( $p_relationship_id, $p_issue_id );

	# user can access to the related bug at least as viewer, if it's exist...
	if( bug_exists( $t_dest_issue_id ) ) {
		if( !access_has_bug_level( VIEWER, $t_dest_issue_id, $t_user_id ) ) {
			return mci_soap_fault_access_denied( $t_user_id , "The issue '$t_dest_issue_id' requires higher access level." );
		}
	}

	$t_bug_relationship_data = relationship_get( $p_relationship_id );
	$t_rel_type = $t_bug_relationship_data->type;

	# delete relationship from the DB
	relationship_delete( $p_relationship_id );

	# update bug last updated
	bug_update_date( $p_issue_id );
	bug_update_date ( $t_dest_issue_id );

	# set the rel_type for both bug and dest_bug based on $t_rel_type and on who is the dest bug
	if( $p_issue_id == $t_bug_relationship_data->src_bug_id ) {
		$t_bug_rel_type = $t_rel_type;
		$t_dest_bug_rel_type = relationship_get_complementary_type( $t_rel_type );
	} else {
		$t_bug_rel_type = relationship_get_complementary_type( $t_rel_type );
		$t_dest_bug_rel_type = $t_rel_type;
	}

	# send email and update the history for the src issue
	history_log_event_special( $p_issue_id, BUG_DEL_RELATIONSHIP, $t_bug_rel_type, $t_dest_issue_id );
	email_relationship_deleted( $p_issue_id, $t_dest_issue_id, $t_bug_rel_type );

	if( bug_exists( $t_dest_issue_id ) ) {

		# send email and update the history for the dest issue
		history_log_event_special( $t_dest_issue_id, BUG_DEL_RELATIONSHIP, $t_dest_bug_rel_type, $p_issue_id );
		email_relationship_deleted( $t_dest_issue_id, $p_issue_id, $t_dest_bug_rel_type );
	}

	return true;
}

/**
 * Log a checkin event on the issue
 *
 * @param string $p_username  The name of the user trying to access the issue.
 * @param string $p_password  The password of the user.
 * @param integer $p_issue_id The id of the issue to log a checkin.
 * @param string $p_comment   The comment to add
 * @param boolean $p_fixed    True if the issue is to be set to fixed
 * @return boolean  true success, false otherwise.
 */
function mc_issue_checkin( $p_username, $p_password, $p_issue_id, $p_comment, $p_fixed ) {
	$t_user_id = mci_check_login( $p_username, $p_password );
	if( $t_user_id === false ) {
		return mci_soap_fault_login_failed();
	}

	if( !bug_exists( $p_issue_id ) ) {
		return new soap_fault( 'Client', '', "Issue '$p_issue_id' not found." );
	}

	$t_project_id = bug_get_field( $p_issue_id, 'project_id' );
	if( !mci_has_readwrite_access( $t_user_id, $t_project_id ) ) {
		return mci_soap_fault_access_denied( $t_user_id );
	}

	helper_call_custom_function( 'checkin', array( $p_issue_id, $p_comment, '', '', $p_fixed ) );

	return true;
}

/**
 * Returns the date in iso8601 format, with proper timezone offset applied
 *
 * @param string $p_date the date in iso8601 format
 * @return int the timestamp
 */
function mci_iso8601_to_timestamp( $p_date ) {

	// retrieve the offset, seems to be lost by nusoap
	$t_utc_date = new DateTime( $p_date, new DateTimeZone( 'UTC' ) );
	$t_timezone = new DateTimeZone( date_default_timezone_get() );
	$t_offset = $t_timezone->getOffset( $t_utc_date );

	$t_raw_timestamp = iso8601_to_timestamp( $p_date );

	return $t_raw_timestamp - $t_offset;

}

/**
 * Returns an array for SOAP encoding from a BugData object
 *
 * @param BugData $p_issue_data
 * @param int $p_user_id
 * @param string $p_lang
 * @return array The issue as an array
 */
function mci_issue_data_as_array( $p_issue_data, $p_user_id, $p_lang ) {

		$t_id = $p_issue_data->id;

		$t_issue = array();
		$t_issue['id'] = $t_id;
		$t_issue['view_state'] = mci_enum_get_array_by_id( $p_issue_data->view_state, 'view_state', $p_lang );
		$t_issue['last_updated'] = timestamp_to_iso8601( $p_issue_data->last_updated );

		$t_issue['project'] = mci_project_as_array_by_id( $p_issue_data->project_id );
		$t_issue['category'] = mci_get_category( $p_issue_data->category_id );
		$t_issue['priority'] = mci_enum_get_array_by_id( $p_issue_data->priority, 'priority', $p_lang );
		$t_issue['severity'] = mci_enum_get_array_by_id( $p_issue_data->severity, 'severity', $p_lang );
		$t_issue['status'] = mci_enum_get_array_by_id( $p_issue_data->status, 'status', $p_lang );

		$t_issue['reporter'] = mci_account_get_array_by_id( $p_issue_data->reporter_id );
		$t_issue['summary'] = $p_issue_data->summary;
		$t_issue['version'] = mci_null_if_empty( $p_issue_data->version );
		$t_issue['build'] = mci_null_if_empty( $p_issue_data->build );
		$t_issue['platform'] = mci_null_if_empty( $p_issue_data->platform );
		$t_issue['os'] = mci_null_if_empty( $p_issue_data->os );
		$t_issue['os_build'] = mci_null_if_empty( $p_issue_data->os_build );
		$t_issue['reproducibility'] = mci_enum_get_array_by_id( $p_issue_data->reproducibility, 'reproducibility', $p_lang );
		$t_issue['date_submitted'] = timestamp_to_iso8601( $p_issue_data->date_submitted );
		$t_issue['sponsorship_total'] = $p_issue_data->sponsorship_total;

		if( !empty( $p_issue_data->handler_id ) ) {
			$t_issue['handler'] = mci_account_get_array_by_id( $p_issue_data->handler_id );
		}
		$t_issue['projection'] = mci_enum_get_array_by_id( $p_issue_data->projection, 'projection', $p_lang );
		$t_issue['eta'] = mci_enum_get_array_by_id( $p_issue_data->eta, 'eta', $p_lang );

		$t_issue['resolution'] = mci_enum_get_array_by_id( $p_issue_data->resolution, 'resolution', $p_lang );
		$t_issue['fixed_in_version'] = mci_null_if_empty( $p_issue_data->fixed_in_version );
		$t_issue['target_version'] = mci_null_if_empty( $p_issue_data->target_version );

		$t_issue['description'] = bug_get_text_field( $t_id, 'description' );

		$t_steps_to_reproduce = bug_get_text_field( $t_id, 'steps_to_reproduce' );
		$t_issue['steps_to_reproduce'] = mci_null_if_empty( $t_steps_to_reproduce );

		$t_additional_information = bug_get_text_field( $t_id, 'additional_information' );
		$t_issue['additional_information'] = mci_null_if_empty( $t_additional_information );

		$t_issue['attachments'] = mci_issue_get_attachments( $p_issue_data->id );
		$t_issue['relationships'] = mci_issue_get_relationships( $p_issue_data->id, $p_user_id );
		$t_issue['notes'] = mci_issue_get_notes( $p_issue_data->id );
		$t_issue['custom_fields'] = mci_issue_get_custom_fields( $p_issue_data->id );

		return $t_issue;
}


text:
package com.github.alezhka.wavesj.utils.crypto.ratchet;

import com.github.alezhka.wavesj.utils.crypto.Curve25519;
import com.github.alezhka.wavesj.utils.crypto.primitives.digest.SHA256;
import com.github.alezhka.wavesj.utils.crypto.primitives.kdf.HKDF;

public class RatchetRootChainKey {
    public static byte[] makeRootChainKey(RatchetPrivateKey ownEphermal,
                                          RatchetPublicKey theirEphermal,
                                          byte[] masterSecret) {

        byte[] ecRes = Curve25519.calculateAgreement(ownEphermal.getPrivateKey(),
                theirEphermal.getKey());

        HKDF hkdf = new HKDF(new SHA256());
        return hkdf.deriveSecrets(ecRes, masterSecret, "ActorRatchet".getBytes(), 32);
    }
}

redpajama_set_name: RedPajamaGithub


text:
//
// Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

/**
* @file      pso.hpp
* @brief     dx12 pso implementation
*/

#include <pso.hpp>

using std::uint32_t;

namespace dx12u
{

    pipeline_state_object::pipeline_state_object(device const& d, input_layout_list const& ill,
        root_signature const& rs, shader_blob const& vs, shader_blob const& hs, shader_blob const& ds, shader_blob const& gs, shader_blob const& ps)
        : ill_copy{ ill }, dev {
        d
    }, rs_copy{ rs }, vs_copy{ vs }, hs_copy{ hs }, ds_copy{ ds }, gs_copy{ gs }, ps_copy{ ps }
    {
        if (d.Get() == nullptr)
        {
            throw error{ "pso creation failed. null device" };
        }

        pso_desc.InputLayout = { nullptr, 0 };
        if (!ill_copy.empty())
        {
            pso_desc.InputLayout = { ill_copy.data(), static_cast<uint32_t>(ill_copy.size()) };
        }
        pso_desc.pRootSignature = rs_copy.Get();
        pso_desc.VS = vs_copy;
        pso_desc.HS = hs_copy;
        pso_desc.DS = ds_copy;
        pso_desc.GS = gs_copy;
        pso_desc.PS = ps_copy;
        pso_desc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
        pso_desc.RasterizerState.FrontCounterClockwise = true;
        pso_desc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
        pso_desc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
        pso_desc.SampleMask = 0xffffffffu;
        pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
        pso_desc.NumRenderTargets = 1;
        pso_desc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
        pso_desc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
        pso_desc.SampleDesc.Count = 1;
    }

    pipeline_state_object::pipeline_state_object(pipeline_state_object&& other) noexcept
        : dev{ other.dev } , pso { other.pso }, pso_desc(other.pso_desc), rs_copy{ other.rs_copy },
        vs_copy{ other.vs_copy }, hs_copy{ other.hs_copy }, ds_copy{ other.ds_copy }, gs_copy{ other.gs_copy }, ps_copy{ other.ps_copy },
        ill_copy{ std::move(ill_copy) }
    {
        other.rs_copy = nullptr;
        other.dev = nullptr;
        other.pso = nullptr;
        other.vs_copy = other.hs_copy = other.ds_copy = other.gs_copy = other.ps_copy = shader_blob{};
        ill_copy = {};
    }

    pipeline_state_object& pipeline_state_object::operator = (pipeline_state_object&& other) noexcept
    {
        if (this != &other)
        {
            dev = other.dev;
            pso = other.pso;
            rs_copy = other.rs_copy;
            pso_desc = other.pso_desc;
            vs_copy = other.vs_copy;
            hs_copy = other.hs_copy;
            ds_copy = other.ds_copy;
            gs_copy = other.gs_copy;
            ps_copy = other.ps_copy;
            ill_copy = std::move(other.ill_copy);
            other.dev = nullptr;
            other.pso = nullptr;
            other.rs_copy = nullptr;
            other.vs_copy = shader_blob{};
            other.hs_copy = shader_blob{};
            other.ds_copy = shader_blob{};
            other.gs_copy = shader_blob{};
            other.ps_copy = shader_blob{};
            other.ill_copy = {};
        }

        return *this;
    }

    void pipeline_state_object::set_gpu_node_mask(std::uint32_t mask)
    {
        if (pso_desc.NodeMask != mask)
        {
            std::unique_lock<std::mutex> lk{ mux };
            pso = nullptr;
        }
        pso_desc.NodeMask = mask;
    }

    void pipeline_state_object::set_depth_stencil_states(D3D12_DEPTH_STENCIL_DESC const & dss)
    {
        if (memcmp(&dss, &pso_desc.DepthStencilState, sizeof(dss)) == 0)
        {
            std::unique_lock<std::mutex> lk{ mux };
            pso = nullptr;
        }
        pso_desc.DepthStencilState = dss;
    }

    void pipeline_state_object::set_blend_states(D3D12_BLEND_DESC const & bs)
    {
        if (memcmp(&bs, &pso_desc.BlendState, sizeof(bs)) == 0)
        {
            std::unique_lock<std::mutex> lk{ mux };
            pso = nullptr;
        }
        pso_desc.BlendState = bs;
    }

    void pipeline_state_object::set_cull_mode(D3D12_CULL_MODE mode)
    {
        if (pso_desc.RasterizerState.CullMode != mode)
        {
            std::unique_lock<std::mutex> lk{ mux };
            pso = nullptr;
        }
        pso_desc.RasterizerState.CullMode = mode;
    }

    void pipeline_state_object::set_ccw(bool ccw)
    {
        if (pso_desc.RasterizerState.FrontCounterClockwise != static_cast<BOOL>(ccw))
        {
            std::unique_lock<std::mutex> lk{ mux };
            pso = nullptr;
        }
        pso_desc.RasterizerState.FrontCounterClockwise = ccw;
    }

    void pipeline_state_object::set_fill_mode(D3D12_FILL_MODE mode)
    {
        if (pso_desc.RasterizerState.FillMode != mode)
        {
            std::unique_lock<std::mutex> lk{ mux };
            pso = nullptr;
        }
        pso_desc.RasterizerState.FillMode = mode;
    }

    void pipeline_state_object::set_depth_test(bool state)
    {
        if (!!pso_desc.DepthStencilState.DepthEnable != state)
        {
            std::unique_lock<std::mutex> lk{ mux };
            pso = nullptr;
        }
        pso_desc.DepthStencilState.DepthEnable = state;
    }

    void pipeline_state_object::set_depth_write(bool state)
    {
        auto s = state ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO;
        if (pso_desc.DepthStencilState.DepthWriteMask != s)
        {
            std::unique_lock<std::mutex> lk{ mux };
            pso = nullptr;
        }
        pso_desc.DepthStencilState.DepthWriteMask = s;
    }

    void pipeline_state_object::set_depth_func(D3D12_COMPARISON_FUNC f)
    {
        if (pso_desc.DepthStencilState.DepthFunc != f)
        {
            std::unique_lock<std::mutex> lk{ mux };
            pso = nullptr;
        }
        pso_desc.DepthStencilState.DepthFunc = f;
    }

    void pipeline_state_object::set_depth_format(DXGI_FORMAT fmt)
    {
        if (pso_desc.DSVFormat != fmt)
        {
            std::unique_lock<std::mutex> lk{ mux };
            pso = nullptr;
        }
        pso_desc.DSVFormat = fmt;
    }

    pipeline_state_object::pso_com_ptr const& pipeline_state_object::get_com_ptr()
    {
        std::unique_lock<std::mutex> lk{ mux };

        if (pso.Get() != nullptr)
        {
            return pso;
        }

        if (!vs_copy.is_valid())
        {
            throw error{ "pso creation failed. At least a root signature and a vs have to be valid" };
        }

        pso = nullptr; // make sure the previous pso is deleted
        auto r = dev->CreateGraphicsPipelineState(&pso_desc, IID_PPV_ARGS(&pso));
        assert(pso.Get() != nullptr);
        dx12u::throw_if_error(r, "pso creation failed. ");

        return pso;
    }

    void pipeline_state_object::set_rt_format(DXGI_FORMAT format, std::size_t rt_index)
    {
        if (rt_index > 7)
        {
            throw error{ "invalid render target index" };
        }
        if (pso_desc.RTVFormats[rt_index] != format)
        {
            std::unique_lock<std::mutex> lk{ mux };
            pso = nullptr;
        }
        pso_desc.RTVFormats[rt_index] = format;
    }

    void pipeline_state_object::release_blob()
    {
        vs_copy.release();
        hs_copy.release();
        ds_copy.release();
        gs_copy.release();
        ps_copy.release();
    }

    void bind(ID3D12GraphicsCommandList* cl, pipeline_state_object& pso)
    {
        auto pso_ptr = pso.get_com_ptr().Get();
        auto rs_ptr = pso.get_root_signature().Get();
        bind(cl, rs_ptr, pso_ptr);
    }

    void bind(ID3D12GraphicsCommandList* cl, ID3D12RootSignature* rs, ID3D12PipelineState* pso)
    {
        assert(pso != nullptr && rs != nullptr && cl != nullptr);
        cl->SetPipelineState(pso);
        cl->SetGraphicsRootSignature(rs);
    }

    input_layout_list get_default_input_layout()
    {
        return
        {
            { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
            { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
            { "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
            { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
            { "INSTANCEPOS", 0, DXGI_FORMAT_R32G32B32_FLOAT, 1, 0, D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1 }
        };
    }

} // namespace dx12u


text:
jsGeneticAlgorithm
==================

This is an implementation of a genetic algorithm in Javascript. Genetic algorithms are sometimes called Evolutionary Algorithms and you can read a lot about them on Wikipedia.

Usage
-----

The entire algorithm (and documentation) is contained within the file jsGeneticAlgorithm.js. You can execute the code in various ways: Using your browser's Javascript console, using a Javascript engine, or by embedding it into an HTML site. Most people will probably embed the code in an HTML site. Several example HTML files are included in the repository. The following is a quick tour through the example files covering everything you can do with this class.

### Example 1 ###

```html
<html><body>
	<script src="jsGeneticAlgorithm.js"></script>
	<pre><script>
		var ga = GeneticAlgorithm();
		var fittest = ga.getFittest();
		document.write('Fittest genome = {' + fittest.join(', ') + '}');
		document.write('<br>');
		document.write('Sum of fittest genome = ' + (function () { for (var i=0, sum=0; i<fittest.length; i++) {sum+=fittest[i];} return sum; })())
	</script></pre>
</body></html>
```

This is the most basic use case. It's not very useful but it will work. The genetic algorithm will run with default parameters on a default problem.

The default problem is to find a list of eight numbers whose sum is equal 5.6. The population size equals 100 individuals, fitness proportional selection is used during the recombination stage, a mutation rate of 5% is applied on a per-gene basis, and elitism with an elite size of 50 individuals is exercised. The algorithm terminates after 100 generations.

The returned variable `ga` contains an object exposing a number of functions to query information about the genetic algorithm run. The sample uses `ga.getFittest()` to retrieve the genome of the fittest individual. Other functions currently available are `ga.getMaxFitnessHistory()` and `ga.getAvgFitnessHistory()`.

### Example 2 ###

```html
<html><body>
	<script src="jsGeneticAlgorithm.js"></script>
	<pre><script>
		var ga = GeneticAlgorithm({
			popSize: 50,
			mutationRate: 0.10,
			eliteSize: 20,
			crossoverMethod: '1-point',
			maxGenerationCount: 30
		});
		var fittest = ga.getFittest();
		document.write('Fittest genome = {' + fittest.join(', ') + '}');
		document.write('<br>');
		document.write('Sum of fittest genome = ' + (function () { for (var i=0, sum=0; i<fittest.length; i++) {sum+=fittest[i];} return sum; })())
	</script></pre>
</body></html>
```

This example changes some of the parameters used in the genetic algorithm but is otherwise identical with example 1. We reduce the population size, reduce the elite size and only complete half the number of generations. We also chose `1-point` crossover instead of the default `2-point` crossover. Generally, you would expect worse results than example 1 because overall less individuals are evaluated.

### Example 3 ###

Travelling Salesman. To be written.

### Example 4 ###

```html
<html><body>
	<script src="jsGeneticAlgorithm.js"></script>
	<canvas id="fitnessHistoryCanvas" width="1000" height="102"></canvas>
	<pre><script>
		// This is a helper function used in randomFunction() and mutationFunction() passed to the GeneticAlgorithm.
		// Due to closure, this function will be available inside the Genetic Algorithm for initializing the first
		// generation andmutations.
		var addChildren = function (parentId, depth, ind) {
			// each node has a maximum of six children nodes
			for (var i=0; i<6; i++) {
				if (Math.random() < Math.pow(0.6, depth)) {
					// create a node with random numerical value
					ind.push({parent:parentId, value:Math.random()*2-1});
					// now add children recursively
					addChildren(ind.length-1, depth+1, ind);
				}
			}
		};

		// This function evaluates the sum of the products obtained by multiplying every leaf with all of its
		// ancestor nodes in the tree. The function is used in the fitness function and later when the fittest
		// individual is displayed (after the genetic algorithm has run).
		// Due to closure, this function will be available inside the Genetic Algorithm.
		var evaluateTree = function (ind) {
			// find all leaves
			var leaveNodes = [];
			for (var i=0; i<ind.length; i++) {
				leaveNodes.push(true);
			}
			for (i=ind.length-1; i>=0; i--) {
				leaveNodes[ind[i].parent] = false;
			}
			// evaluate the numerical value encoded in the tree
			var treeSum = 0;
			for (i=0; i<ind.length; i++) {
				if (leaveNodes[i]) {
					var product = ind[i].value, parentPointer = ind[i].parent;
					while (parentPointer !== undefined) {
						product *= ind[parentPointer].value;
						parentPointer = ind[parentPointer].parent;
					}
					treeSum += product;
				}
			}

			return treeSum;
		};

		// This is where we define the parameters of the genetic algorithm and the problem specific functions. All
		// available parameters, even if the specified value equals the default value. For an explanation of the
		// each parameter, see the inline documentation in jsGeneticAlgorithm.js.
		var ga = GeneticAlgorithm({
			popSize: 100,
			randomFunction: function () {
					// create genome with a top node with random numerical value and no parent
					var ind = [{parent:undefined, value: Math.random()*20-10}];
					// recursively add children nodes
					addChildren(0, 1, ind);

					return ind;
				},
			mutationFunction: function (ind) {
					// validate that the individual is valid
					if (! ind || !ind.length || ind.length < 1) return ind;
					// decide which node to mutate
					var k = Math.floor(Math.random()*ind.length);
					// find all decendant nodes of this node and remove them
					for (var j=ind.length-1; j>k; j--) {
						var isSubNode = false, parentPointer = ind[j].parent;
						while (parentPointer >= k) {
							if (parentPointer == k) {
								isSubNode = true;
								break;
							}
							parentPointer = ind[parentPointer].parent;
						}
						if (isSubNode) {
							ind.slice(j, j+1);
						}
					}
					// give the mutated node an arbitrary new numerical value
					ind[k].value = Math.random()*2-1;
					// find at what depth node k is
					var depth = 0;
					parentPointer = ind[k].parent;
					while (parentPointer != undefined) {
						depth++;
						parentPointer = ind[parentPointer].parent;
					}
					// add a new random subtree to node k
					addChildren(k, depth, ind);

					return ind;
				},
			mutationRate: 0.10,
			selectionMethod: 'rank-prop',
			tournamentSize: undefined,
			eliteSize: 50,
			crossoverMethod: '1-point',
			fitnessFunction: function (ind) {
					// find the difference to pi/10 and return the inverse of its absolute value as fitness
					return 1/Math.abs(Math.PI/10 - evaluateTree(ind));
				},
			stopCondition: 'generation-count',
			maxGenerationCount: 1000,
			maxStaticGenerations: undefined,
			debug: 0
		});

		// When code execution arrives here, the genetic algorithm has run and found a solution.

		// It is now possible to retrieve statistics about the evolution run. Let's use the canvas to
		// display a time history of maximum and average fitness.
		var maxFitnessHistory = ga.getMaxFitnessHistory();
		var avgFitnessHistory = ga.getAvgFitnessHistory();
		var maxFitnessEver = maxFitnessHistory[maxFitnessHistory.length-1];
		var canvas = document.getElementById('fitnessHistoryCanvas');
		var ctx = canvas.getContext("2d");
		for (var i=0; i<maxFitnessHistory.length; i=i+1) {
			ctx.fillStyle = "rgb(0,0,200)";
			ctx.fillRect (i/1, 100-Math.ceil(avgFitnessHistory[i]/maxFitnessEver*97), 1, 2);
			ctx.fillStyle = "rgb(200,0,0)";
			ctx.fillRect (i/1, 100-Math.ceil(maxFitnessHistory[i]/maxFitnessEver*97), 1, 2);
		}

		// It is also possible to query for the fittest individual discovered
		var fittest = ga.getFittest();
		document.write('Fittest genome evaluates to ' + evaluateTree(fittest) + ' resulting in a fitness of ' + maxFitnessHistory[maxFitnessHistory.length-1] + '<br>');

		// display the tree described by the genome of the fittest individual
		var depths = [0];
		for (var i=0; i<fittest.length; i++) {
			document.write('[' + i + '] ' + fittest[i].value + ', parent: ' + fittest[i].parent + '<br>');
		}
	</script></pre>
</body></html>
```

This example illustrates how to use genomes that are not simply an array of numbers. With a suitable set of initialization, muatation, and fitness evaluation functions, each individual's genome may consist of an array of arbitrary datatype(s).

Here we use a tree structure with every node representing one floating point number between -1 and 1, one example tree is shown here:

![Visual representation of a genome as a tree of connected numbers.](https://raw.github.com/jonemo/jsGeneticAlgorithm/master/docs/graph_individual.png "Visual representation of a genome as a tree of connected numbers.")

The fitness of an individual is evaluated by multiplying every tree leaf with all of its parent nodes, summing up the result and comparing the result to π/10. The fitness is the inverse of the difference squared. For the tree above the fitness would be: ``` 1 / [ (4.15)(-5.32)(0.30)+(8.66)(-1.92)(-5.32)(0.30)+(3.76)(4.01)(8.02)(0.30)+(1.59)(8.02)(0.30)+(-2.93)(8.02)(0.30)+(1.78)(-0.95)(-0.37)(-3.47)(0.30)+(5.45)(-0.37)(-3.47)(0.30)+(5.07)(-3.47)(0.30)+(-2.88)(0.04)(4.34)(0.03) - π/10] ^ 2 = 0.00047``` Note that the general idea is to have a fitness metric, that is always bigger than zero and always has a gradient towards a maximum. If a large group of individuals has the same fitness, a genetic algorithm might not be a good way for finding a maximum in the fitness landscape.

This fitness metric used in this example is arbitrary and as far as I can tell it's pretty non-sensical. Remember, the purpose of this example is simply to show that an array of any datatype can be used for the genome. It is also not necessary, that all genomes have the same length!

To encode such a tree in a genome, every node becomes an entry in the array and stores its numeric value and references its parent node. The function that creates such trees randomly is shown in this snippet:

```javascript
randomFunction: function () {
		// create genome with a top node with random numerical value and no parent
		var ind = [{parent:undefined, value: Math.random()*20-10}];
		// recursively add children nodes
		addChildren(0, 1, ind);

		return ind;
	},
```

It creates a first node (every tree has at least one node) and then uses ```addChildren()``` to recursively add child nodes. The probabilty of child nodes reduces with the depth of tree, to avoid infinite or very long trees.

The mutation function looks more complex but is actually conceptually very similar:

```javascript
mutationFunction: function (ind) {
		// validate that the individual is valid
		if (! ind || !ind.length || ind.length < 1) return ind;
		// decide which node to mutate
		var k = Math.floor(Math.random()*ind.length);
		// find all decendant nodes of this node and remove them
		for (var j=ind.length-1; j>k; j--) {
			var isSubNode = false, parentPointer = ind[j].parent;
			while (parentPointer >= k) {
				if (parentPointer == k) {
					isSubNode = true;
					break;
				}
				parentPointer = ind[parentPointer].parent;
			}
			if (isSubNode) {
				ind.slice(j, j+1);
			}
		}
		// give the mutated node an arbitrary new numerical value
		ind[k].value = Math.random()*2-1;
		// find at what depth node k is
		var depth = 0;
		parentPointer = ind[k].parent;
		while (parentPointer != undefined) {
			depth++;
			parentPointer = ind[parentPointer].parent;
		}
		// add a new random subtree to node k
		addChildren(k, depth, ind);

		return ind;
	},
```

It randomly picks one node in the tree and removes it from the tree together with the subtree attached to it. Again using the ```addChildren()``` function, a random subtree is created in its place. Remember that the ```mutationRate``` parameter defines how likely it is, that any individual experiences a single mutation.

In this example we use rank-proportional selection, a population size of 100 individuals, half of which transfer directly to the next generation as members of the "elite".

Development Status
---------------------

This code is currently in development. More documentation is forthcoming. Additional functionality will be added as I need them for my own purposes or on request.


text:
class AddProgrammmingLanguageExtension < ActiveRecord::Migration
  def self.up
    #add_column( :programming_languages, :extension, :string, :null => false )
  end

  def self.down
    #remove_column( :programming_languages, :extension )
  end
end


text:
using System;
using System.Windows;

namespace VKPlayer.Forms
{
    /// <summary>
    /// Interaction logic for VKAuthorization.xaml
    /// </summary>
    public partial class VKAuthorization : Window
    {
        public event Action<string, string, string> ConfirmClicked;

        public VKAuthorization()
        {
            InitializeComponent();
        }

        private void confirmButton_Click(object sender, RoutedEventArgs e) { ConfirmClicked?.Invoke(loginTextBox.Text, passTextBox.Password, twofacorTextBox.Text); }
    }
}