spingle/source/mathlib.c

485 lines
11 KiB
C
Raw Normal View History

2019-11-24 20:45:15 -08:00
/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
This program 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 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// mathlib.c -- math primitives
2019-12-02 07:07:37 -08:00
#include "q_defs.h"
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
vec3_t vec3_origin = {0, 0, 0};
2019-11-24 20:45:15 -08:00
/*-----------------------------------------------------------------*/
2019-12-02 04:24:20 -08:00
#define DEG2RAD( a ) ( (a) * PI_DIV_180 ) //johnfitz
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
void ProjectPointOnPlane(vec3_t dst, const vec3_t p, const vec3_t normal)
2019-11-24 20:45:15 -08:00
{
float d;
vec3_t n;
float inv_denom;
2019-11-25 17:40:18 -08:00
inv_denom = 1.0F / DotProduct(normal, normal);
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
d = DotProduct(normal, p) * inv_denom;
2019-11-24 20:45:15 -08:00
n[0] = normal[0] * inv_denom;
n[1] = normal[1] * inv_denom;
n[2] = normal[2] * inv_denom;
dst[0] = p[0] - d * n[0];
dst[1] = p[1] - d * n[1];
dst[2] = p[2] - d * n[2];
}
/*
** assumes "src" is normalized
*/
2019-11-25 17:40:18 -08:00
void PerpendicularVector(vec3_t dst, const vec3_t src)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t pos;
2019-11-25 16:49:58 -08:00
int32_t i;
2019-11-24 20:45:15 -08:00
float minelem = 1.0F;
vec3_t tempvec;
/*
** find the smallest magnitude axially aligned vector
*/
2019-11-25 17:40:18 -08:00
for(pos = 0, i = 0; i < 3; i++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(fabs(src[i]) < minelem)
2019-11-24 20:45:15 -08:00
{
pos = i;
2019-11-25 17:40:18 -08:00
minelem = fabs(src[i]);
2019-11-24 20:45:15 -08:00
}
}
tempvec[0] = tempvec[1] = tempvec[2] = 0.0F;
tempvec[pos] = 1.0F;
/*
** project the point onto the plane defined by src
*/
2019-11-25 17:40:18 -08:00
ProjectPointOnPlane(dst, tempvec, src);
2019-11-24 20:45:15 -08:00
/*
** normalize the result
*/
2019-11-25 17:40:18 -08:00
VectorNormalize(dst);
2019-11-24 20:45:15 -08:00
}
//johnfitz -- removed RotatePointAroundVector() becuase it's no longer used and my compiler fucked it up anyway
/*-----------------------------------------------------------------*/
2019-11-25 17:40:18 -08:00
float anglemod(float a)
2019-11-24 20:45:15 -08:00
{
#if 0
2019-11-25 17:40:18 -08:00
if(a >= 0)
a -= 360 * (int32_t)(a / 360);
2019-11-24 20:45:15 -08:00
else
2019-11-25 17:40:18 -08:00
a += 360 * (1 + (int32_t)(-a / 360));
2019-11-24 20:45:15 -08:00
#endif
2019-11-25 17:40:18 -08:00
a = (360.0 / 65536) * ((int32_t)(a * (65536 / 360.0)) & 65535);
2019-11-24 20:45:15 -08:00
return a;
}
/*
==================
BoxOnPlaneSide
Returns 1, 2, or 1 + 2
==================
*/
2019-11-25 17:40:18 -08:00
int32_t BoxOnPlaneSide(vec3_t emins, vec3_t emaxs, mplane_t *p)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
float dist1, dist2;
int32_t sides;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
#if 0 // this is done by the BOX_ON_PLANE_SIDE macro before calling this
// function
2019-11-24 20:45:15 -08:00
// fast axial cases
2019-11-25 17:40:18 -08:00
if(p->type < 3)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(p->dist <= emins[p->type])
2019-11-24 20:45:15 -08:00
return 1;
2019-11-25 17:40:18 -08:00
if(p->dist >= emaxs[p->type])
2019-11-24 20:45:15 -08:00
return 2;
return 3;
}
#endif
// general case
2019-11-25 17:40:18 -08:00
switch(p->signbits)
2019-11-24 20:45:15 -08:00
{
case 0:
2019-11-25 17:40:18 -08:00
dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2];
dist2 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2];
2019-11-24 20:45:15 -08:00
break;
case 1:
2019-11-25 17:40:18 -08:00
dist1 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2];
dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2];
2019-11-24 20:45:15 -08:00
break;
case 2:
2019-11-25 17:40:18 -08:00
dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2];
dist2 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2];
2019-11-24 20:45:15 -08:00
break;
case 3:
2019-11-25 17:40:18 -08:00
dist1 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2];
dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2];
2019-11-24 20:45:15 -08:00
break;
case 4:
2019-11-25 17:40:18 -08:00
dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2];
dist2 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2];
2019-11-24 20:45:15 -08:00
break;
case 5:
2019-11-25 17:40:18 -08:00
dist1 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2];
dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2];
2019-11-24 20:45:15 -08:00
break;
case 6:
2019-11-25 17:40:18 -08:00
dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2];
dist2 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2];
2019-11-24 20:45:15 -08:00
break;
case 7:
2019-11-25 17:40:18 -08:00
dist1 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2];
dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2];
2019-11-24 20:45:15 -08:00
break;
default:
2019-11-25 17:40:18 -08:00
dist1 = dist2 = 0; // shut up compiler
Sys_Error("BoxOnPlaneSide: Bad signbits");
2019-11-24 20:45:15 -08:00
break;
}
#if 0
2019-11-25 17:40:18 -08:00
int32_t i;
vec3_t corners[2];
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < 3 ; i++)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(plane->normal[i] < 0)
2019-11-24 20:45:15 -08:00
{
corners[0][i] = emins[i];
corners[1][i] = emaxs[i];
}
else
{
corners[1][i] = emins[i];
corners[0][i] = emaxs[i];
}
}
2019-11-25 17:40:18 -08:00
dist = DotProduct(plane->normal, corners[0]) - plane->dist;
dist2 = DotProduct(plane->normal, corners[1]) - plane->dist;
2019-11-24 20:45:15 -08:00
sides = 0;
2019-11-25 17:40:18 -08:00
if(dist1 >= 0)
2019-11-24 20:45:15 -08:00
sides = 1;
2019-11-25 17:40:18 -08:00
if(dist2 < 0)
2019-11-24 20:45:15 -08:00
sides |= 2;
#endif
sides = 0;
2019-11-25 17:40:18 -08:00
if(dist1 >= p->dist)
2019-11-24 20:45:15 -08:00
sides = 1;
2019-11-25 17:40:18 -08:00
if(dist2 < p->dist)
2019-11-24 20:45:15 -08:00
sides |= 2;
2019-12-02 04:24:20 -08:00
#if defined(PARANOID)
2019-11-25 17:40:18 -08:00
if(sides == 0)
Sys_Error("BoxOnPlaneSide: sides==0");
2019-11-24 20:45:15 -08:00
#endif
return sides;
}
//johnfitz -- the opposite of AngleVectors. this takes forward and generates pitch yaw roll
//TODO: take right and up vectors to properly set yaw and roll
2019-11-25 17:40:18 -08:00
void VectorAngles(const vec3_t forward, vec3_t angles)
2019-11-24 20:45:15 -08:00
{
vec3_t temp;
temp[0] = forward[0];
temp[1] = forward[1];
temp[2] = 0;
2019-12-02 04:24:20 -08:00
angles[PITCH] = -atan2(forward[2], VectorLength(temp)) / PI_DIV_180;
angles[YAW] = atan2(forward[1], forward[0]) / PI_DIV_180;
2019-11-24 20:45:15 -08:00
angles[ROLL] = 0;
}
2019-11-25 17:40:18 -08:00
void AngleVectors(vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
float angle;
float sr, sp, sy, cr, cp, cy;
2019-11-24 20:45:15 -08:00
2019-12-02 04:24:20 -08:00
angle = angles[YAW] * (PI * 2 / 360);
2019-11-24 20:45:15 -08:00
sy = sin(angle);
cy = cos(angle);
2019-12-02 04:24:20 -08:00
angle = angles[PITCH] * (PI * 2 / 360);
2019-11-24 20:45:15 -08:00
sp = sin(angle);
cp = cos(angle);
2019-12-02 04:24:20 -08:00
angle = angles[ROLL] * (PI * 2 / 360);
2019-11-24 20:45:15 -08:00
sr = sin(angle);
cr = cos(angle);
2019-11-25 17:40:18 -08:00
forward[0] = cp * cy;
forward[1] = cp * sy;
2019-11-24 20:45:15 -08:00
forward[2] = -sp;
2019-11-25 17:40:18 -08:00
right[0] = (-1 * sr * sp * cy + -1 * cr * -sy);
right[1] = (-1 * sr * sp * sy + -1 * cr * cy);
right[2] = -1 * sr * cp;
up[0] = (cr * sp * cy + -sr * -sy);
up[1] = (cr * sp * sy + -sr * cy);
up[2] = cr * cp;
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
int32_t VectorCompare(vec3_t v1, vec3_t v2)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t i;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
for(i = 0 ; i < 3 ; i++)
if(v1[i] != v2[i])
2019-11-24 20:45:15 -08:00
return 0;
return 1;
}
2019-11-25 17:40:18 -08:00
void VectorMA(vec3_t veca, float scale, vec3_t vecb, vec3_t vecc)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
vecc[0] = veca[0] + scale * vecb[0];
vecc[1] = veca[1] + scale * vecb[1];
vecc[2] = veca[2] + scale * vecb[2];
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
vec_t _DotProduct(vec3_t v1, vec3_t v2)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
void _VectorSubtract(vec3_t veca, vec3_t vecb, vec3_t out)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
out[0] = veca[0] - vecb[0];
out[1] = veca[1] - vecb[1];
out[2] = veca[2] - vecb[2];
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
void _VectorAdd(vec3_t veca, vec3_t vecb, vec3_t out)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
out[0] = veca[0] + vecb[0];
out[1] = veca[1] + vecb[1];
out[2] = veca[2] + vecb[2];
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
void _VectorCopy(vec3_t in, vec3_t out)
2019-11-24 20:45:15 -08:00
{
out[0] = in[0];
out[1] = in[1];
out[2] = in[2];
}
2019-11-25 17:40:18 -08:00
void CrossProduct(vec3_t v1, vec3_t v2, vec3_t cross)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
cross[0] = v1[1] * v2[2] - v1[2] * v2[1];
cross[1] = v1[2] * v2[0] - v1[0] * v2[2];
cross[2] = v1[0] * v2[1] - v1[1] * v2[0];
2019-11-24 20:45:15 -08:00
}
vec_t VectorLength(vec3_t v)
{
2019-11-25 17:40:18 -08:00
return sqrt(DotProduct(v, v));
2019-11-24 20:45:15 -08:00
}
2019-11-25 17:40:18 -08:00
float VectorNormalize(vec3_t v)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
float length, ilength;
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
length = sqrt(DotProduct(v, v));
2019-11-24 20:45:15 -08:00
2019-11-25 17:40:18 -08:00
if(length)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
ilength = 1 / length;
2019-11-24 20:45:15 -08:00
v[0] *= ilength;
v[1] *= ilength;
v[2] *= ilength;
}
return length;
}
2019-11-25 17:40:18 -08:00
void VectorInverse(vec3_t v)
2019-11-24 20:45:15 -08:00
{
v[0] = -v[0];
v[1] = -v[1];
v[2] = -v[2];
}
2019-11-25 17:40:18 -08:00
void VectorScale(vec3_t in, vec_t scale, vec3_t out)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
out[0] = in[0] * scale;
out[1] = in[1] * scale;
out[2] = in[2] * scale;
2019-11-24 20:45:15 -08:00
}
2019-11-25 16:49:58 -08:00
int32_t Q_log2(int32_t val)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t answer = 0;
while(val >>= 1)
2019-11-24 20:45:15 -08:00
answer++;
return answer;
}
/*
================
R_ConcatRotations
================
*/
2019-11-25 17:40:18 -08:00
void R_ConcatRotations(float in1[3][3], float in2[3][3], float out[3][3])
2019-11-24 20:45:15 -08:00
{
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
2019-11-25 17:40:18 -08:00
in1[0][2] * in2[2][0];
2019-11-24 20:45:15 -08:00
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
2019-11-25 17:40:18 -08:00
in1[0][2] * in2[2][1];
2019-11-24 20:45:15 -08:00
out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
2019-11-25 17:40:18 -08:00
in1[0][2] * in2[2][2];
2019-11-24 20:45:15 -08:00
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
2019-11-25 17:40:18 -08:00
in1[1][2] * in2[2][0];
2019-11-24 20:45:15 -08:00
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
2019-11-25 17:40:18 -08:00
in1[1][2] * in2[2][1];
2019-11-24 20:45:15 -08:00
out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
2019-11-25 17:40:18 -08:00
in1[1][2] * in2[2][2];
2019-11-24 20:45:15 -08:00
out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
2019-11-25 17:40:18 -08:00
in1[2][2] * in2[2][0];
2019-11-24 20:45:15 -08:00
out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
2019-11-25 17:40:18 -08:00
in1[2][2] * in2[2][1];
2019-11-24 20:45:15 -08:00
out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
2019-11-25 17:40:18 -08:00
in1[2][2] * in2[2][2];
2019-11-24 20:45:15 -08:00
}
/*
================
R_ConcatTransforms
================
*/
2019-11-25 17:40:18 -08:00
void R_ConcatTransforms(float in1[3][4], float in2[3][4], float out[3][4])
2019-11-24 20:45:15 -08:00
{
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
2019-11-25 17:40:18 -08:00
in1[0][2] * in2[2][0];
2019-11-24 20:45:15 -08:00
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
2019-11-25 17:40:18 -08:00
in1[0][2] * in2[2][1];
2019-11-24 20:45:15 -08:00
out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
2019-11-25 17:40:18 -08:00
in1[0][2] * in2[2][2];
2019-11-24 20:45:15 -08:00
out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] +
2019-11-25 17:40:18 -08:00
in1[0][2] * in2[2][3] + in1[0][3];
2019-11-24 20:45:15 -08:00
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
2019-11-25 17:40:18 -08:00
in1[1][2] * in2[2][0];
2019-11-24 20:45:15 -08:00
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
2019-11-25 17:40:18 -08:00
in1[1][2] * in2[2][1];
2019-11-24 20:45:15 -08:00
out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
2019-11-25 17:40:18 -08:00
in1[1][2] * in2[2][2];
2019-11-24 20:45:15 -08:00
out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] +
2019-11-25 17:40:18 -08:00
in1[1][2] * in2[2][3] + in1[1][3];
2019-11-24 20:45:15 -08:00
out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
2019-11-25 17:40:18 -08:00
in1[2][2] * in2[2][0];
2019-11-24 20:45:15 -08:00
out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
2019-11-25 17:40:18 -08:00
in1[2][2] * in2[2][1];
2019-11-24 20:45:15 -08:00
out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
2019-11-25 17:40:18 -08:00
in1[2][2] * in2[2][2];
2019-11-24 20:45:15 -08:00
out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] +
2019-11-25 17:40:18 -08:00
in1[2][2] * in2[2][3] + in1[2][3];
2019-11-24 20:45:15 -08:00
}
/*
===================
FloorDivMod
Returns mathematically correct (floor-based) quotient and remainder for
numer and denom, both of which should contain no fractional part. The
quotient must fit in 32 bits.
====================
*/
2019-11-25 17:40:18 -08:00
void FloorDivMod(double numer, double denom, int32_t *quotient,
int32_t *rem)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
int32_t q, r;
double x;
2019-11-24 20:45:15 -08:00
2019-12-02 04:24:20 -08:00
#if !defined(PARANOID)
2019-11-25 17:40:18 -08:00
if(denom <= 0.0)
Sys_Error("FloorDivMod: bad denominator %f\n", denom);
2019-11-24 20:45:15 -08:00
// if ((floor(numer) != numer) || (floor(denom) != denom))
// Sys_Error ("FloorDivMod: non-integer numer or denom %f %f\n",
// numer, denom);
#endif
2019-11-25 17:40:18 -08:00
if(numer >= 0.0)
2019-11-24 20:45:15 -08:00
{
x = floor(numer / denom);
2019-11-25 16:49:58 -08:00
q = (int32_t)x;
r = (int32_t)floor(numer - (x * denom));
2019-11-24 20:45:15 -08:00
}
else
{
2019-11-25 17:40:18 -08:00
//
// perform operations with positive values, and fix mod to make floor-based
//
2019-11-24 20:45:15 -08:00
x = floor(-numer / denom);
2019-11-25 16:49:58 -08:00
q = -(int32_t)x;
r = (int32_t)floor(-numer - (x * denom));
2019-11-25 17:40:18 -08:00
if(r != 0)
2019-11-24 20:45:15 -08:00
{
q--;
2019-11-25 16:49:58 -08:00
r = (int32_t)denom - r;
2019-11-24 20:45:15 -08:00
}
}
*quotient = q;
*rem = r;
}
/*
===================
GreatestCommonDivisor
====================
*/
2019-11-25 17:40:18 -08:00
int32_t GreatestCommonDivisor(int32_t i1, int32_t i2)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(i1 > i2)
2019-11-24 20:45:15 -08:00
{
2019-11-25 17:40:18 -08:00
if(i2 == 0)
2019-11-24 20:45:15 -08:00
return (i1);
2019-11-25 17:40:18 -08:00
return GreatestCommonDivisor(i2, i1 % i2);
2019-11-24 20:45:15 -08:00
}
else
{
2019-11-25 17:40:18 -08:00
if(i1 == 0)
2019-11-24 20:45:15 -08:00
return (i2);
2019-11-25 17:40:18 -08:00
return GreatestCommonDivisor(i1, i2 % i1);
2019-11-24 20:45:15 -08:00
}
}