/* ************************************************************************** */
/*                                                                            */
/*                                                        :::      ::::::::   */
/*   ft_safe_long_math.c                                :+:      :+:    :+:   */
/*                                                    +:+ +:+         +:+     */
/*   By: thrieg <thrieg@student.42mulhouse.fr>      +#+  +:+       +#+        */
/*                                                +#+#+#+#+#+   +#+           */
/*   Created: 2024/11/08 11:11:17 by alier             #+#    #+#             */
/*   Updated: 2025/02/16 19:04:26 by thrieg           ###   ########.fr       */
/*                                                                            */
/* ************************************************************************** */

#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
#include <errno.h>

/* 
 * Does saturate and never overflows/underflows.
 * Sets `errno` to `ERANGE` if the operation would
 * have caused an overflow/underflow.
 */
long	safe_long_add(long a, long b)
{
	if (b >= 0 && a > LONG_MAX - b)
	{
		errno = ERANGE;
		return (LONG_MAX);
	}
	else if (b < 0 && a < LONG_MIN - b)
	{
		errno = ERANGE;
		return (LONG_MIN);
	}
	return (a + b);
}

/* 
 * Does saturate and never overflows/underflows.
 * Sets `errno` to `ERANGE` if the operation would
 * have caused an overflow/underflow.
 */
long	safe_long_sub(long a, long b)
{
	if (b >= 0 && a < LONG_MIN + b)
	{
		errno = ERANGE;
		return (LONG_MIN);
	}
	else if (b < 0 && a > LONG_MAX + b)
	{
		errno = ERANGE;
		return (LONG_MAX);
	}
	return (a - b);
}

/* 
 * Does saturate and never overflows/underflows.
 * Sets `errno` to `ERANGE` if the operation would
 * have caused an overflow/underflow.
 */
long	safe_long_mul(long a, long b)
{
	if (b > 0)
	{
		if (a > 0 && a > LONG_MAX / b)
			return (errno = ERANGE, LONG_MAX);
		else if (a < 0 && a < LONG_MIN / b)
			return (errno = ERANGE, LONG_MIN);
	}
	else if (b < 0)
	{
		if (a < 0 && a < LONG_MAX / b)
			return (errno = ERANGE, LONG_MAX);
		else if ((-LONG_MAX > LONG_MIN && b < -1) && a > LONG_MIN / b)
			return (errno = ERANGE, LONG_MIN);
	}
	return (a * b);
}
