<commit_before>/*
 * Copyright (c) 2013-2017 <NAME>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "vimol.h"

struct frame {
	struct atoms *atoms;
};

struct atom {
	char name[64];
	vec_t xyz;
};

struct sys {
	int current_frame;
	int is_modified;
	struct graph *graph;
	struct sel *sel;
	struct sel *visible;
	int nframes, nframesalloc;
	struct frame *frames;
};

static const vec_t h_table[] = {
	{  1.090,  0.000,  0.000 }, /*  0 C(sp3) H-1 */
	{ -0.360,  1.029,  0.000 }, /*  1 C(sp3) H-2 */
	{ -0.350, -0.523,  0.890 }, /*  2 C(sp3) H-3 */
	{ -0.350, -0.523, -0.890 }, /*  3 C(sp3) H-4 */
	{ -0.542,  0.946,  0.000 }, /*  4 C(sp2) H-1 */
	{ -0.542, -0.946,  0.000 }, /*  5 C(sp2) H-2 */
	{ -1.090,  0.000,  0.000 }, /*  6 C(sp)  H-1 */
	{  1.070,  0.000,  0.000 }, /*  7 N      H-1 */
	{ -0.353,  1.010,  0.000 }, /*  8 N      H-2 */
	{ -0.350, -0.510,  0.873 }, /*  9 N      H-3 */
	{  1.380,  0.000,  0.000 }, /* 10 P      H-1 */
	{ -0.455,  1.303,  0.000 }, /* 11 P      H-2 */
	{ -0.451, -0.658,  1.126 }, /* 12 P      H-3 */
	{  1.050,  0.000,  0.000 }, /* 13 O      H-1 */
	{ -0.350,  0.990,  0.000 }, /* 14 O      H-2 */
	{  1.340,  0.000,  0.000 }, /* 15 S      H-1 */
	{ -0.447,  1.263,  0.000 }  /* 16 S      H-2 */
};

static struct atoms *
sys_get_atoms(struct sys *sys, int frame)
{
	return (sys->frames[frame].atoms);
}

static void
sys_add_frame(struct sys *sys, struct atoms *atoms)
{
	if (sys->nframes == sys->nframesalloc) {
		sys->nframesalloc *= 2;
		sys->frames = xrealloc(sys->frames,
		    sys->nframesalloc * sizeof(struct frame));
	}
	sys->frames[sys->nframes].atoms = atoms;
	sys->nframes++;
}

static int
sys_single_frame(struct sys *sys)
{
	return (sys_get_frame_count(sys) == 1);
}

static void
add_hydrogens(struct sys *sys, int i, int j, int k, int offset, int count)
{
	mat_t rotmat;
	vec_t pi, pj, pk;

	pi = sys_get_atom_xyz(sys, i);

	if (j < 0) {
		pj = vec_random();
		pj = vec_add(&pj, &pi);
	} else
		pj = sys_get_atom_xyz(sys, j);

	if (k < 0) {
		pk = vec_random();
		pk = vec_add(&pk, &pi);
	} else
		pk = sys_get_atom_xyz(sys, k);

	rotmat = mat_align(&pi, &pj, &pk);

	for (k = 0; k < count; k++) {
		pk = h_table[offset + k];
		pk = mat_vec(&rotmat, &pk);
		pk = vec_add(&pk, &pi);

		sys_add_atom(sys, "H", pk);

		j = sys_get_atom_count(sys) - 1;
		graph_edge_create(sys->graph, i, j, 1);
	}
}

static int
is_hydrogen(const char *name)
{
	return (toupper(name[0]) == 'H' && name[1] == '\0');
}

static int
get_bond_count(struct graph *graph, int idx)
{
	struct graphedge *edge;
	int count = 0;

	for (edge = graph_edges(graph, idx); edge; edge = graph_edge_next(edge))
		count += graph_edge_get_type(edge);

	return (count);
}

static int
parse_atom_pdb(const char *buffer, struct atom *atom)
{
	size_t i, j, buflen;

	if ((buflen = strlen(buffer)) < 54) {
		error_set("incorrect pdb atom record format");
		return (0);
	}

	memset(atom, 0, sizeof(struct atom));

	if (buflen < 78) {
		for (i = 12, j = 0; i < 16; i++)
			if (!isspace(buffer[i]))
				atom->name[j++] = buffer[i];
	} else {
		for (i = 76, j = 0; i < 78; i++)
			if (!isspace(buffer[i]))
				atom->name[j++] = buffer[i];
	}

	if (atom->name[0] == '\0')
		atom->name[0] = 'X';

	sscanf(&buffer[30], "%lf%lf%lf", &atom->xyz.x, &atom->xyz.y,
	    &atom->xyz.z);
	return (1);
}

static int
load_from_pdb(struct sys *sys, const char *path)
{
	FILE *fp;
	char *buffer;
	struct atom atom;

	if ((fp = fopen(path, "r")) == NULL) {
		error_set("unable to open %s", path);
		return (0);
	}

	buffer = NULL;

	while ((buffer = util_next_line(buffer, fp)) != NULL) {
		if (strncasecmp(buffer, "ATOM  ", 6) == 0 ||
		    strncasecmp(buffer, "HETATM", 6) == 0) {
			if (!parse_atom_pdb(buffer, &atom))
				goto error;

			sys_add_atom(sys, atom.name, atom.xyz);
		}
	}

	fclose(fp);
	return (1);
error:
	free(buffer);
	fclose(fp);
	return (0);
}

static int
parse_atom_xyz(const char *buffer, struct atom *atom)
{
	memset(atom, 0, sizeof(struct atom));

	sscanf(buffer, "%32s%lf%lf%lf", atom->name, &atom->xyz.x,
	    &atom->xyz.y, &atom->xyz.z);
	return (1);
}

static int
load_from_xyz(struct sys *sys, const char *path, int is_new)
{
	FILE *fp;
	char *buffer;
	struct atoms *atoms;
	struct atom atom;
	int i, n;

	if ((fp = fopen(path, "r")) == NULL) {
		error_set("unable to open %s", path);
		return (0);
	}

	if ((buffer = util_next_line(NULL, fp)) == NULL) {
		error_set("unexpected end of file");
		goto error;
	}

	if (sscanf(buffer, "%d", &n) != 1 || n < 1) {
		error_set("unexpected number of atoms");
		goto error;
	}

	buffer = util_next_line(buffer, fp);

	for (i = 0; i < n; i++) {
		if ((buffer = util_next_line(buffer, fp)) == NULL) {
			error_set("unexpected end of file");
			goto error;
		}

		if (!parse_atom_xyz(buffer, &atom))
			goto error;

		sys_add_atom(sys, atom.name, atom.xyz);
	}

	if (!is_new) {
		free(buffer);
		fclose(fp);
		return (1);
	}

	while ((buffer = util_next_line(buffer, fp)) != NULL) {
		if (string_is_whitespace(buffer))
			continue;

		if (sscanf(buffer, "%d", &n) != 1 ||
		    n != sys_get_atom_count(sys)) {
			error_set("unexpected number of atoms");
			goto error;
		}

		sys_add_frame(sys, atoms_create());
		sys_set_frame(sys, sys_get_frame_count(sys) - 1);

		buffer = util_next_line(buffer, fp);

		for (i = 0; i < n; i++) {
			if ((buffer = util_next_line(buffer, fp)) == NULL) {
				error_set("unexpected end of file");
				goto error;
			}

			if (!parse_atom_xyz(buffer, &atom))
				goto error;

			atoms = sys_get_atoms(sys, sys->current_frame);
			atoms_add(atoms, atom.name, atom.xyz);
		}
	}

	sys_set_frame(sys, 0);
	fclose(fp);
	return (1);
error:
	free(buffer);
	fclose(fp);
	return (0);
}

static int
load_file(struct sys *sys, const char *path, int is_new)
{<commit_msg>Allow multiframe pdb files.<commit_after>/*
 * Copyright (c) 2013-2017 <NAME>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "vimol.h"

struct frame {
	struct atoms *atoms;
};

struct atom {
	char name[64];
	vec_t xyz;
};

struct sys {
	int current_frame;
	int is_modified;
	struct graph *graph;
	struct sel *sel;
	struct sel *visible;
	int nframes, nframesalloc;
	struct frame *frames;
};

static const vec_t h_table[] = {
	{  1.090,  0.000,  0.000 }, /*  0 C(sp3) H-1 */
	{ -0.360,  1.029,  0.000 }, /*  1 C(sp3) H-2 */
	{ -0.350, -0.523,  0.890 }, /*  2 C(sp3) H-3 */
	{ -0.350, -0.523, -0.890 }, /*  3 C(sp3) H-4 */
	{ -0.542,  0.946,  0.000 }, /*  4 C(sp2) H-1 */
	{ -0.542, -0.946,  0.000 }, /*  5 C(sp2) H-2 */
	{ -1.090,  0.000,  0.000 }, /*  6 C(sp)  H-1 */
	{  1.070,  0.000,  0.000 }, /*  7 N      H-1 */
	{ -0.353,  1.010,  0.000 }, /*  8 N      H-2 */
	{ -0.350, -0.510,  0.873 }, /*  9 N      H-3 */
	{  1.380,  0.000,  0.000 }, /* 10 P      H-1 */
	{ -0.455,  1.303,  0.000 }, /* 11 P      H-2 */
	{ -0.451, -0.658,  1.126 }, /* 12 P      H-3 */
	{  1.050,  0.000,  0.000 }, /* 13 O      H-1 */
	{ -0.350,  0.990,  0.000 }, /* 14 O      H-2 */
	{  1.340,  0.000,  0.000 }, /* 15 S      H-1 */
	{ -0.447,  1.263,  0.000 }  /* 16 S      H-2 */
};

static struct atoms *
sys_get_atoms(struct sys *sys, int frame)
{
	return (sys->frames[frame].atoms);
}

static void
sys_add_frame(struct sys *sys, struct atoms *atoms)
{
	if (sys->nframes == sys->nframesalloc) {
		sys->nframesalloc *= 2;
		sys->frames = xrealloc(sys->frames,
		    sys->nframesalloc * sizeof(struct frame));
	}
	sys->frames[sys->nframes].atoms = atoms;
	sys->nframes++;
}

static int
sys_single_frame(struct sys *sys)
{
	return (sys_get_frame_count(sys) == 1);
}

static void
add_hydrogens(struct sys *sys, int i, int j, int k, int offset, int count)
{
	mat_t rotmat;
	vec_t pi, pj, pk;

	pi = sys_get_atom_xyz(sys, i);

	if (j < 0) {
		pj = vec_random();
		pj = vec_add(&pj, &pi);
	} else
		pj = sys_get_atom_xyz(sys, j);

	if (k < 0) {
		pk = vec_random();
		pk = vec_add(&pk, &pi);
	} else
		pk = sys_get_atom_xyz(sys, k);

	rotmat = mat_align(&pi, &pj, &pk);

	for (k = 0; k < count; k++) {
		pk = h_table[offset + k];
		pk = mat_vec(&rotmat, &pk);
		pk = vec_add(&pk, &pi);

		sys_add_atom(sys, "H", pk);

		j = sys_get_atom_count(sys) - 1;
		graph_edge_create(sys->graph, i, j, 1);
	}
}

static int
is_hydrogen(const char *name)
{
	return (toupper(name[0]) == 'H' && name[1] == '\0');
}

static int
get_bond_count(struct graph *graph, int idx)
{
	struct graphedge *edge;
	int count = 0;

	for (edge = graph_edges(graph, idx); edge; edge = graph_edge_next(edge))
		count += graph_edge_get_type(edge);

	return (count);
}

static int
parse_atom_pdb(const char *buffer, struct atom *atom)
{
	size_t i, j, buflen;

	if ((buflen = strlen(buffer)) < 54) {
		error_set("incorrect pdb atom record format");
		return (0);
	}

	memset(atom, 0, sizeof(struct atom));

	if (buflen < 78) {
		for (i = 12, j = 0; i < 16; i++)
			if (!isspace(buffer[i]))
				atom->name[j++] = buffer[i];
	} else {
		for (i = 76, j = 0; i < 78; i++)
			if (!isspace(buffer[i]))
				atom->name[j++] = buffer[i];
	}

	if (atom->name[0] == '\0')
		atom->name[0] = 'X';

	sscanf(&buffer[30], "%lf%lf%lf", &atom->xyz.x, &atom->xyz.y,
	    &atom->xyz.z);
	return (1);
}

static int
load_from_pdb(struct sys *sys, const char *path)
{
	FILE *fp;
	struct atoms *atoms;
	struct atom atom;
	char *buffer;
	int i, newframe = 0;

	if ((fp = fopen(path, "r")) == NULL) {
		error_set("unable to open %s", path);
		return (0);
	}

	buffer = NULL;

	while ((buffer = util_next_line(buffer, fp)) != NULL) {
		if (strncasecmp(buffer, "ATOM  ", 6) == 0 ||
		    strncasecmp(buffer, "HETATM", 6) == 0) {
			if (newframe) {
				sys_add_frame(sys, atoms_create());
				sys_set_frame(sys, sys_get_frame_count(sys)-1);
				newframe = 0;
			}
			if (!parse_atom_pdb(buffer, &atom))
				goto error;
			if (sys_get_frame_count(sys) == 1)
				sys_add_atom(sys, atom.name, atom.xyz);
			else {
				atoms = sys_get_atoms(sys, sys->current_frame);
				atoms_add(atoms, atom.name, atom.xyz);
			}
		}
		if (strncasecmp(buffer, "END", 3) == 0)
			newframe = 1;
	}

	sys_set_frame(sys, 0);

	for (i = 0; i < sys_get_frame_count(sys); i++)
		if (atoms_get_count(sys->frames[0].atoms) !=
		    atoms_get_count(sys->frames[i].atoms)) {
			error_set("unexpected number of atoms");
			goto error;
		}

	fclose(fp);
	return (1);
error:
	free(buffer);
	fclose(fp);
	return (0);
}

static int
parse_atom_xyz(const char *buffer, struct atom *atom)
{
	memset(atom, 0, sizeof(struct atom));

	sscanf(buffer, "%32s%lf%lf%lf", atom->name, &atom->xyz.x,
	    &atom->xyz.y, &atom->xyz.z);
	return (1);
}

static int
load_from_xyz(struct sys *sys, const char *path, int is_new)
{
	FILE *fp;
	char *buffer;
	struct atoms *atoms;
	struct atom atom;
	int i, n;

	if ((fp = fopen(path, "r")) == NULL) {
		error_set("unable to open %s", path);
		return (0);
	}

	if ((buffer = util_next_line(NULL, fp)) == NULL) {
		error_set("unexpected end of file");
		goto error;
	}

	if (sscanf(buffer, "%d", &n) != 1 || n < 1) {
		error_set("unexpected number of atoms");
		goto error;
	}

	buffer = util_next_line(buffer, fp);

	for (i = 0; i < n; i++) {
		if ((buffer = util_next_line(buffer, fp)) == NULL) {
			error_set("unexpected end of file");
			goto error;
		}

		if (!parse_atom_xyz(buffer, &atom))
			goto error;

		sys_add_atom(sys, atom.name, atom.xyz);
	}

	if (!is_new) {
		free(buffer);
		fclose(fp);
		return (1);
	}

	while ((buffer = util_next_line(buffer, fp)) != NULL) {
		if (string_is_whitespace(buffer))
			continue;

		if (sscanf(buffer, "%d", &n) != 1 ||
		    n != sys_get_atom_count(sys)) {
			error_set("unexpected number of atoms");
			goto error;
		}

		sys_add_frame(sys, atoms_create());
		sys_set_frame(sys, sys_get_frame_count(sys)-1);

		buffer = util_next_line(buffer, fp);

		for (i = 0; i < n; i++) {
			if ((buffer = util_next_line(buffer, fp)) == NULL) {
				error_set("unexpected end of file");
				goto error;
			}

			if (!parse_atom_xyz(buffer, &atom))
				goto error;

			atoms = sys_get_atoms(sys, sys->current_frame);
			atoms_add(atoms, atom.name, atom.xyz);
		}
	}

	sys_set_frame(sys, 0);
	fclose(fp);
	return (1);
error:
	free(buffer);
	fclose(fp);
	return (0);
}

static int
load_file(struct sys *sys, const char *path, int is_new)
{
{'fraction_non_alphanumeric': 0.1783387622149837, 'fraction_numerical': 0.04752739117559965, 'mean_word_length': 3.132456408687672, 'pattern_counts': {'":': 0, '<': 26, '<?xml version=': 0, '>': 54, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '383525', 'n_tokens_mistral': 6293, 'n_tokens_neox': 5562, 'n_words': 1495}