/*****
 NAME
	cint.m - source code for CInt class
 VERSION
	$Id$
 CHANGELOG
	$Log$
 */

#include <coconut/cint.h>
#include <coconut/cstring.h>
#include <coconut/csystem.h>
#include <coconut/cerror.h>
#include <coconut/ptext.h>
#include <coconut/fbasic.h>
#include <stdlib.h>
#include <time.h>
#include <limits.h>
#include <errno.h>

@implementation CInt

+ initRandomSeed
{
	srand(time(0)) ;
	return nil ;
}

+ (int) getRandomInt: (int) min to: (int) max
{
	double dmax = (double) max ;
	/* this expression is copied from linux manual page for rand() */
	return min + (int) (dmax * rand() / (RAND_MAX+1.0)) ;
}

+ (id <PError>) binStrToUInt64: (const char *) str to: (u_int64_t *) result
{
	u_int64_t	val = 0 ;
	char 		c ;
	int		len ;

	len = strlen(str) ;

	/* skip first "0b" or "0B" */
	if(len >= 2){
		if(str[0] == '0' && (str[1] == 'b' || str[1] == 'B')){
			str += 2 ; len -= 2 ;
		}
	}
	if(len < 1){
		return [CError illegal_parameter] ;
	}
	/* skip next '0' codes */
	for( ; *str == '0' ; str++, len--)
		;
	/* check bit number */
	if(len > 64){
		return [CError overflow] ;
	}
	/* get values */
	for( ; (c = *str) != '\0' ; str++){
		if(c == '0'){
			val = val << 1 ;
		} else if(c == '1'){
			val = val << 1 | 0x01 ;
		} else {
			return [CError illegal_parameter] ;
		}
	}
	*result = val ;
	return nil ;
}

+ (id <PError>) strToULong: (const char *) str to: (u_long *) result
{
	char *	endp ;
	if(str == NULL || result == NULL)
		return [CError illegal_parameter] ;
	*result = strtoul(str, &endp, 10) ;
	if(*endp == '\0' && errno == 0){
		return nil ;
	} else {
		return [CError illegal_parameter] ;
	}
}

+ (id <PError>) strToInt: (const char *) str to: (int *) result
{
	char *	endp ;
	long	lval ;

	if(str == NULL || result == NULL)
		return [CError illegal_parameter] ;
	lval = strtol(str, &endp, 10) ;
	if(*endp == '\0' && IS_IN_INT_RANGE(lval)){
		*result = lval ;
		return nil ;
	} else {
		return [CError illegal_range] ;
	}
}

+ (id <PError>) uInt64ToBinStr: (u_int64_t) value to: (char *) dst
{
	u_int64_t	mask ;

	/*       0123456789abcdef */
	mask = 0x8000000000000000UL ;

	/* set header "0b" */
	dst[0] = '0' ; dst[1] = 'b' ; dst += 2 ;
	/* skip spaces */
	for( ; mask != 0L && (value & mask) == 0L ; mask >>= 1)
		;
	if(mask == 0L){
		dst[0] = '0' ; dst[1] = '\0' ;
	} else {
		while(mask != 0L){
			*(dst++) = (value & mask) ? '1' : '0' ;
			mask >>= 1 ; 
		}
		*dst = '\0' ;
	}
	return nil ;
}

+ (id <PNumber>) newInt: (int) intval format: (int_format_t) form 
{
	id <PNumber>	num ;

	num = [[CInt alloc] initInt: intval format: form] ;
	g_assert(num != nil) ;
	return num ;
}

- initInt: (int) intval format: (int_format_t) form
{
	int_value = intval ;
	int_format = form ;
	return [super init] ;
}

- init
{
	int_value = 0 ;
	int_format = unknown_format ;
	return [super init] ;
}

- (void) dealloc
{
	return [super dealloc] ;
}

- (valtype_t) type
{
	return VALTYPE_INT ;
}

- setObject: (id <PNumber>) object
{
	int_value = [object intNum] ;
	return nil ;
}

- setBoolean: (boolean) val
{
	int_value = val ? 1 : 0 ; 
	return nil ;
}

- setInt: (int) val
{
	int_value = val ;
	return nil ;
}

- setReal: (double) val
{
	int_value = val ;
	return nil ;
}

- (boolean) booleanNum
{
	return int_value ? TRUE : FALSE ;
}

- (int) intNum
{
	return int_value ;
}

- (double) realNum
{
	return int_value ;
}

- (id <PNumber>) addObject: (id <PNumber>) obj
{
	int_value += [obj intNum] ;
	return self ;
}

- (id <PNumber>) addBoolean: (boolean) val
{
	int_value += val ? 1 : 0 ;
	return self ;
}

- (id <PNumber>) addInt: (int) val
{
	int_value += val ;
	return self ;
}

- (id <PNumber>) addReal: (double) val
{
	int_value += val ;
	return self ;
}

- (id <PNumber>) duplicate
{
	return [CInt newInt: int_value format: int_format] ;
}

- (id <PString>) toString
{
	id <PString>	newstr ;

	newstr = [[CString alloc] init] ;
	[CSystem checkPtr: newstr] ;
	[newstr setFormat: "((int) %d)", int_value] ;
	return newstr ;
}

#define	INTDIFF(A, B)		(((int) A) - ((int) B))
- (int) compare: (id <PNumber>) src
{
	int	delta ;

	if((delta = INTDIFF(VALTYPE_INT, [src type])) != 0)
		return delta ;
	delta = int_value - [src intNum] ;
	return delta ;
}

- (u_int) hashkey
{
	return (((int) VALTYPE_INT) << 8) + int_value ;
}

#define	STREAM	((id <PIndentStream>) stream)
- print: (id) stream
{
	io_status_t	stat ;
	u_int64_t	uval ;
	char *		formstr ;
	const char *	conststr ;
	char 		binstr[67] ;

	if(int_format == bin_format){
		formstr = binstr ;
		if(int_value >= 0){
			uval = int_value ;
		} else {
			uval = -int_value ;
			*(formstr++) = '-' ;
		}
		[CInt uInt64ToBinStr: uval to: formstr] ;
		stat = [STREAM putFormat: "%s", formstr] ;
	} else {
		conststr = intFormat2PrintFormat(int_format) ;
		stat = [STREAM putFormat: conststr, int_value] ;
	}
	return stat == io_status_normal ? nil : [CError can_not_happen] ;
}

@end

