Dynamic Fraction Library 1.0.0
Reference-counted arbitrary precision rational number library (MIT OR Unlicense)
Loading...
Searching...
No Matches
dynamic_fraction.h
Go to the documentation of this file.
1
45#ifndef DYNAMIC_FRACTION_H
46#define DYNAMIC_FRACTION_H
47
48#include <stdint.h>
49#include <stddef.h>
50#include <stdbool.h>
51#include <limits.h>
52#include <math.h>
53#include <string.h>
54
55// Include dynamic_int.h for arbitrary precision integers
56#include "devDeps/dynamic_int.h"
57
58/* Configuration macros */
59#ifndef DF_MALLOC
60#define DF_MALLOC malloc
61#endif
62
63#ifndef DF_FREE
64#define DF_FREE free
65#endif
66
67#ifndef DF_ASSERT
68#define DF_ASSERT assert
69#endif
70
71// API macros
72#ifdef DF_STATIC
73#define DF_DEF static
74#define DF_IMPL static
75#else
76#define DF_DEF extern
77#define DF_IMPL /* nothing - default linkage */
78#endif
79
80// ============================================================================
81// INTERFACE
82// ============================================================================
83
96
101typedef struct df_frac_internal* df_frac;
102
117DF_DEF df_frac df_from_ints(int64_t numerator, int64_t denominator);
118
126
132DF_DEF df_frac df_from_int(int64_t value);
133
140DF_DEF df_frac df_from_double(double value, int64_t max_denominator);
141
147DF_DEF df_frac df_copy(df_frac f);
148
154DF_DEF df_frac df_retain(df_frac f);
155
160DF_DEF void df_release(df_frac* f);
161
// end of lifecycle
163
176DF_DEF df_frac df_add(df_frac a, df_frac b);
177
184DF_DEF df_frac df_sub(df_frac a, df_frac b);
185
192DF_DEF df_frac df_mul(df_frac a, df_frac b);
193
200DF_DEF df_frac df_div(df_frac a, df_frac b);
201
207DF_DEF df_frac df_negate(df_frac f);
208
214DF_DEF df_frac df_abs(df_frac f);
215
222
// end of arithmetic
224
237DF_DEF int df_cmp(df_frac a, df_frac b);
238
245DF_DEF bool df_eq(df_frac a, df_frac b);
246
253DF_DEF bool df_ne(df_frac a, df_frac b);
254
261DF_DEF bool df_lt(df_frac a, df_frac b);
262
269DF_DEF bool df_le(df_frac a, df_frac b);
270
277DF_DEF bool df_gt(df_frac a, df_frac b);
278
285DF_DEF bool df_ge(df_frac a, df_frac b);
286
// end of comparison
288
300DF_DEF bool df_is_zero(df_frac f);
301
307DF_DEF bool df_is_one(df_frac f);
308
314DF_DEF bool df_is_negative(df_frac f);
315
321DF_DEF bool df_is_positive(df_frac f);
322
328DF_DEF bool df_is_integer(df_frac f);
329
// end of predicates
331
343DF_DEF double df_to_double(df_frac f);
344
351DF_DEF bool df_to_int64(df_frac f, int64_t* result);
352
359DF_DEF char* df_to_string(df_frac f);
360
366DF_DEF df_frac df_from_string(const char* str);
367
373DF_DEF di_int df_numerator(df_frac f);
374
381
// end of conversion
383
394DF_DEF df_frac df_zero(void);
395
400DF_DEF df_frac df_one(void);
401
406DF_DEF df_frac df_neg_one(void);
407
// end of special
409
410// ============================================================================
411// EXTENDED OPERATIONS
412// ============================================================================
413
427DF_DEF df_frac df_pow(df_frac base, int64_t exponent);
428
435DF_DEF df_frac df_floor(df_frac f);
436
443DF_DEF df_frac df_ceil(df_frac f);
444
451DF_DEF df_frac df_trunc(df_frac f);
452
459DF_DEF df_frac df_round(df_frac f);
460
467DF_DEF int df_sign(df_frac f);
468
476DF_DEF df_frac df_min(df_frac a, df_frac b);
477
485DF_DEF df_frac df_max(df_frac a, df_frac b);
486
487
494DF_DEF uint64_t df_hash(df_frac f);
495
502DF_DEF bool df_fits_int32(df_frac f);
503
510DF_DEF bool df_fits_int64(df_frac f);
511
518DF_DEF bool df_fits_double(df_frac f);
519
527
535
// end of extended
537
538// ============================================================================
539// IMPLEMENTATION
540// ============================================================================
541
542#ifdef DF_IMPLEMENTATION
543
544#include <stdlib.h>
545#include <string.h>
546#include <math.h>
547#include <assert.h>
548#include <stdio.h>
549
550// Helper: Allocate a new fraction structure
551static df_frac df_alloc(void) {
552 df_frac f = (df_frac)DF_MALLOC(sizeof(struct df_frac_internal));
553 DF_ASSERT(f && "df_alloc: memory allocation failed");
554 f->numerator = NULL;
555 f->denominator = NULL;
556 f->ref_count = 1;
557 return f;
558}
559
560// Helper: Reduce fraction to lowest terms
561static void df_reduce(df_frac f) {
562 DF_ASSERT(f && "df_reduce: fraction cannot be NULL");
563 DF_ASSERT(f->numerator && "df_reduce: numerator cannot be NULL");
564 DF_ASSERT(f->denominator && "df_reduce: denominator cannot be NULL");
565
566 // Get GCD of numerator and denominator
567 di_int gcd = di_gcd(f->numerator, f->denominator);
568 if (!gcd || di_is_one(gcd)) {
569 di_release(&gcd);
570 return;
571 }
572
573 // Divide both by GCD
574 di_int new_num = di_div(f->numerator, gcd);
575 di_int new_den = di_div(f->denominator, gcd);
576
579 f->numerator = new_num;
580 f->denominator = new_den;
581
582 di_release(&gcd);
583}
584
585// Helper: Ensure denominator is positive (move sign to numerator)
586static void df_normalize_sign(df_frac f) {
587 DF_ASSERT(f && "df_normalize_sign: fraction cannot be NULL");
588 DF_ASSERT(f->denominator && "df_normalize_sign: denominator cannot be NULL");
589
590 if (di_is_negative(f->denominator)) {
591 di_int neg_num = di_negate(f->numerator);
592 di_int neg_den = di_negate(f->denominator);
593
596 f->numerator = neg_num;
597 f->denominator = neg_den;
598 }
599}
600
601// Create fraction from int64 values
602DF_IMPL df_frac df_from_ints(int64_t numerator, int64_t denominator) {
603 DF_ASSERT(denominator != 0 && "df_from_ints: denominator cannot be zero");
604
605 df_frac f = df_alloc();
606 DF_ASSERT(f && "df_from_ints: allocation failed");
607
610
611 if (!f->numerator || !f->denominator) {
614 DF_FREE(f);
615 return NULL;
616 }
617
618 df_normalize_sign(f);
619 df_reduce(f);
620 return f;
621}
622
623// Create fraction from di_int values
625 DF_ASSERT(numerator && "df_from_di: numerator cannot be NULL");
626 DF_ASSERT(denominator && !di_is_zero(denominator) && "df_from_di: denominator cannot be zero");
627
628 df_frac f = df_alloc();
629 DF_ASSERT(f && "df_from_ints: allocation failed");
630
633
634 df_normalize_sign(f);
635 df_reduce(f);
636 return f;
637}
638
639// Create fraction from integer
640DF_IMPL df_frac df_from_int(int64_t value) {
641 return df_from_ints(value, 1);
642}
643
644// Create fraction from double using continued fractions
645DF_IMPL df_frac df_from_double(double value, int64_t max_denominator) {
646 if (isnan(value) || isinf(value)) return NULL;
647 if (max_denominator <= 0) max_denominator = INT64_MAX;
648
649 // Handle negative values
650 bool negative = value < 0;
651 if (negative) value = -value;
652
653 // Continued fraction algorithm
654 int64_t h0 = 0, h1 = 1;
655 int64_t k0 = 1, k1 = 0;
656 double x = value;
657
658 while (k1 <= max_denominator) {
659 int64_t a = (int64_t)floor(x);
660 int64_t h2 = a * h1 + h0;
661 int64_t k2 = a * k1 + k0;
662
663 if (k2 > max_denominator) break;
664
665 h0 = h1; h1 = h2;
666 k0 = k1; k1 = k2;
667
668 if (fabs(value - (double)h1 / (double)k1) < 1e-15) break;
669
670 x = 1.0 / (x - a);
671 if (x > 1e15) break; // Avoid overflow
672 }
673
674 return df_from_ints(negative ? -h1 : h1, k1);
675}
676
677// Copy a fraction
679 DF_ASSERT(f && "df_copy: fraction cannot be NULL");
680 di_int num = di_retain(f->numerator);
681 di_int den = di_retain(f->denominator);
682 df_frac result = df_from_di(num, den);
683 di_release(&num);
684 di_release(&den);
685 return result;
686}
687
688// Retain (increase reference count)
690 DF_ASSERT(f && "df_retain: fraction cannot be NULL");
691 f->ref_count++;
692 return f;
693}
694
695// Release (decrease reference count)
696DF_IMPL void df_release(df_frac* f) {
697 if (!f || !*f) return;
698
699 (*f)->ref_count--;
700 if ((*f)->ref_count == 0) {
701 di_release(&(*f)->numerator);
702 di_release(&(*f)->denominator);
703 DF_FREE(*f);
704 }
705 *f = NULL;
706}
707
708// Addition: a/b + c/d = (ad + bc) / bd
710 DF_ASSERT(a && "df_add: first operand cannot be NULL");
711 DF_ASSERT(b && "df_add: second operand cannot be NULL");
712
713 // Calculate ad and bc
714 di_int ad = di_mul(a->numerator, b->denominator);
715 di_int bc = di_mul(b->numerator, a->denominator);
716 di_int num = di_add(ad, bc);
717
718 // Calculate bd
719 di_int den = di_mul(a->denominator, b->denominator);
720
721 df_frac result = df_from_di(num, den);
722
723 di_release(&ad);
724 di_release(&bc);
725 di_release(&num);
726 di_release(&den);
727
728 return result;
729}
730
731// Subtraction: a/b - c/d = (ad - bc) / bd
733 DF_ASSERT(a && "df_sub: first operand cannot be NULL");
734 DF_ASSERT(b && "df_sub: second operand cannot be NULL");
735
736 // Calculate ad and bc
737 di_int ad = di_mul(a->numerator, b->denominator);
738 di_int bc = di_mul(b->numerator, a->denominator);
739 di_int num = di_sub(ad, bc);
740
741 // Calculate bd
742 di_int den = di_mul(a->denominator, b->denominator);
743
744 df_frac result = df_from_di(num, den);
745
746 di_release(&ad);
747 di_release(&bc);
748 di_release(&num);
749 di_release(&den);
750
751 return result;
752}
753
754// Multiplication: (a/b) * (c/d) = ac / bd
756 DF_ASSERT(a && "df_mul: first operand cannot be NULL");
757 DF_ASSERT(b && "df_mul: second operand cannot be NULL");
758
759 di_int num = di_mul(a->numerator, b->numerator);
760 di_int den = di_mul(a->denominator, b->denominator);
761
762 df_frac result = df_from_di(num, den);
763
764 di_release(&num);
765 di_release(&den);
766
767 return result;
768}
769
770// Division: (a/b) / (c/d) = ad / bc
772 DF_ASSERT(a && "df_div: dividend cannot be NULL");
773 DF_ASSERT(b && "df_div: divisor cannot be NULL");
774 DF_ASSERT(!df_is_zero(b) && "df_div: division by zero");
775
776 di_int num = di_mul(a->numerator, b->denominator);
777 di_int den = di_mul(a->denominator, b->numerator);
778
779 df_frac result = df_from_di(num, den);
780
781 di_release(&num);
782 di_release(&den);
783
784 return result;
785}
786
787// Negate
789 DF_ASSERT(f && "df_negate: operand cannot be NULL");
790
791 di_int neg_num = di_negate(f->numerator);
792 di_int den = di_retain(f->denominator);
793 df_frac result = df_from_di(neg_num, den);
794 di_release(&neg_num);
795 di_release(&den);
796
797 return result;
798}
799
800// Absolute value
802 DF_ASSERT(f && "df_abs: operand cannot be NULL");
803
804 di_int abs_num = di_abs(f->numerator);
805 di_int den = di_retain(f->denominator);
806 df_frac result = df_from_di(abs_num, den);
807 di_release(&abs_num);
808 di_release(&den);
809
810 return result;
811}
812
813// Reciprocal
815 DF_ASSERT(f && "df_reciprocal: operand cannot be NULL");
816 DF_ASSERT(!df_is_zero(f) && "df_reciprocal: reciprocal of zero");
817
818 di_int num = di_retain(f->denominator);
819 di_int den = di_retain(f->numerator);
820 df_frac result = df_from_di(num, den);
821 di_release(&num);
822 di_release(&den);
823 return result;
824}
825
826// Compare two fractions
827DF_IMPL int df_cmp(df_frac a, df_frac b) {
828 DF_ASSERT(a && "df_cmp: first operand cannot be NULL");
829 DF_ASSERT(b && "df_cmp: second operand cannot be NULL");
830
831 // Compare a/b with c/d by comparing ad with bc
832 di_int ad = di_mul(a->numerator, b->denominator);
833 di_int bc = di_mul(b->numerator, a->denominator);
834
835 int result = di_compare(ad, bc);
836
837 di_release(&ad);
838 di_release(&bc);
839
840 return result;
841}
842
843// Equality test
844DF_IMPL bool df_eq(df_frac a, df_frac b) {
845 return df_cmp(a, b) == 0;
846}
847
848// Inequality test
849DF_IMPL bool df_ne(df_frac a, df_frac b) {
850 return df_cmp(a, b) != 0;
851}
852
853// Less than
854DF_IMPL bool df_lt(df_frac a, df_frac b) {
855 return df_cmp(a, b) < 0;
856}
857
858// Less than or equal
859DF_IMPL bool df_le(df_frac a, df_frac b) {
860 return df_cmp(a, b) <= 0;
861}
862
863// Greater than
864DF_IMPL bool df_gt(df_frac a, df_frac b) {
865 return df_cmp(a, b) > 0;
866}
867
868// Greater than or equal
869DF_IMPL bool df_ge(df_frac a, df_frac b) {
870 return df_cmp(a, b) >= 0;
871}
872
873// Test if zero
874DF_IMPL bool df_is_zero(df_frac f) {
875 DF_ASSERT(f && "df_is_zero: operand cannot be NULL");
876 return di_is_zero(f->numerator);
877}
878
879// Test if one
880DF_IMPL bool df_is_one(df_frac f) {
881 DF_ASSERT(f && "df_is_one: operand cannot be NULL");
882 return di_eq(f->numerator, f->denominator);
883}
884
885// Test if negative
886DF_IMPL bool df_is_negative(df_frac f) {
887 DF_ASSERT(f && "df_is_negative: operand cannot be NULL");
888 return di_is_negative(f->numerator);
889}
890
891// Test if positive
892DF_IMPL bool df_is_positive(df_frac f) {
893 DF_ASSERT(f && "df_is_positive: operand cannot be NULL");
894 return !di_is_negative(f->numerator) && !di_is_zero(f->numerator);
895}
896
897// Test if integer
898DF_IMPL bool df_is_integer(df_frac f) {
899 DF_ASSERT(f && "df_is_integer: operand cannot be NULL");
900 return di_is_one(f->denominator);
901}
902
903// Convert to double
904DF_IMPL double df_to_double(df_frac f) {
905 DF_ASSERT(f && "df_to_double: operand cannot be NULL");
906
907 double num = di_to_double(f->numerator);
908 double den = di_to_double(f->denominator);
909
910 return num / den;
911}
912
913// Convert to int64 if possible
914DF_IMPL bool df_to_int64(df_frac f, int64_t* result) {
915 DF_ASSERT(f && "df_to_int64: fraction cannot be NULL");
916 DF_ASSERT(result && "df_to_int64: result pointer cannot be NULL");
917 if (!df_is_integer(f)) return false;
918
919 return di_to_int64(f->numerator, result);
920}
921
922// Convert to string
923DF_IMPL char* df_to_string(df_frac f) {
924 DF_ASSERT(f && "df_to_string: operand cannot be NULL");
925
926 char* num_str = di_to_string(f->numerator, 10);
927 DF_ASSERT(num_str && "df_to_string: numerator string conversion failed");
928
929 // If integer, just return the numerator string
930 if (df_is_integer(f)) {
931 return num_str;
932 }
933
934 char* den_str = di_to_string(f->denominator, 10);
935 if (!den_str) {
936 free(num_str);
937 return NULL;
938 }
939
940 // Allocate space for "num/den"
941 size_t len = strlen(num_str) + strlen(den_str) + 2;
942 char* result = (char*)DF_MALLOC(len);
943 DF_ASSERT(result && "df_to_string: result allocation failed");
944
945 snprintf(result, len, "%s/%s", num_str, den_str);
946
947 free(num_str);
948 free(den_str);
949
950 return result;
951}
952
953// Parse from string
954DF_IMPL df_frac df_from_string(const char* str) {
955 DF_ASSERT(str && "df_from_string: string cannot be NULL");
956
957 // Look for '/' separator
958 const char* slash = strchr(str, '/');
959
960 if (!slash) {
961 // No slash, parse as integer
962 di_int num = di_from_string(str, 10);
963 DF_ASSERT(num && "df_from_string: numerator parsing failed");
964
965 di_int den = di_one();
966 df_frac result = df_from_di(num, den);
967
968 di_release(&num);
969 di_release(&den);
970
971 return result;
972 }
973
974 // Parse numerator and denominator
975 size_t num_len = slash - str;
976 char* num_str = (char*)DF_MALLOC(num_len + 1);
977 DF_ASSERT(num_str && "df_from_string: numerator string allocation failed");
978
979 strncpy(num_str, str, num_len);
980 num_str[num_len] = '\0';
981
982 di_int num = di_from_string(num_str, 10);
983 DF_FREE(num_str);
984
985 DF_ASSERT(num && "df_from_string: numerator parsing failed");
986
987 di_int den = di_from_string(slash + 1, 10);
988 DF_ASSERT(den && "df_from_string: denominator parsing failed");
989 DF_ASSERT(!di_is_zero(den) && "df_from_string: denominator cannot be zero");
990
991 df_frac result = df_from_di(num, den);
992
993 di_release(&num);
994 di_release(&den);
995
996 return result;
997}
998
999// Get numerator
1001 DF_ASSERT(f && "df_numerator: operand cannot be NULL");
1002 return di_retain(f->numerator);
1003}
1004
1005// Get denominator
1007 DF_ASSERT(f && "df_denominator: operand cannot be NULL");
1008 return di_retain(f->denominator);
1009}
1010
1011// Create zero
1012DF_IMPL df_frac df_zero(void) {
1013 return df_from_ints(0, 1);
1014}
1015
1016// Create one
1017DF_IMPL df_frac df_one(void) {
1018 return df_from_ints(1, 1);
1019}
1020
1021// Create negative one
1022DF_IMPL df_frac df_neg_one(void) {
1023 return df_from_ints(-1, 1);
1024}
1025
1026// ============================================================================
1027// Extended Functions Implementation
1028// ============================================================================
1029
1030// Power function
1031DF_IMPL df_frac df_pow(df_frac base, int64_t exponent) {
1032 DF_ASSERT(base && "df_pow: base cannot be NULL");
1033
1034 if (exponent == 0) {
1035 return df_one();
1036 }
1037
1038 if (exponent == 1) {
1039 return df_copy(base);
1040 }
1041
1042 if (df_is_zero(base)) {
1043 DF_ASSERT(exponent > 0 && "df_pow: zero to negative power is undefined");
1044 return df_zero();
1045 }
1046
1047 // Handle negative exponents
1048 if (exponent < 0) {
1049 df_frac reciprocal = df_reciprocal(base);
1050 df_frac result = df_pow(reciprocal, -exponent);
1051 df_release(&reciprocal);
1052 return result;
1053 }
1054
1055 // Positive integer exponent - use exponentiation by squaring
1056 df_frac result = df_one();
1057 df_frac current_base = df_copy(base);
1058
1059 while (exponent > 0) {
1060 if (exponent & 1) { // If exponent is odd
1061 df_frac new_result = df_mul(result, current_base);
1062 df_release(&result);
1063 result = new_result;
1064 }
1065 exponent >>= 1;
1066 if (exponent > 0) {
1067 df_frac new_base = df_mul(current_base, current_base);
1068 df_release(&current_base);
1069 current_base = new_base;
1070 }
1071 }
1072
1073 df_release(&current_base);
1074 return result;
1075}
1076
1077// Floor function
1079 DF_ASSERT(f && "df_floor: operand cannot be NULL");
1080
1081 if (df_is_integer(f)) {
1082 return df_copy(f);
1083 }
1084
1085 // di_div already performs floor division (rounds toward negative infinity)
1086 di_int quotient = di_div(f->numerator, f->denominator);
1087
1088 di_int one = di_one();
1089 df_frac result = df_from_di(quotient, one);
1090 di_release(&quotient);
1091 di_release(&one);
1092 return result;
1093}
1094
1095// Ceiling function
1097 DF_ASSERT(f && "df_ceil: operand cannot be NULL");
1098
1099 if (df_is_integer(f)) {
1100 return df_copy(f);
1101 }
1102
1103 // Get integer part (floor)
1104 di_int quotient = di_div(f->numerator, f->denominator);
1105
1106 // For non-integers, ceiling is always floor + 1
1107 di_int one = di_one();
1108 di_int adjusted = di_add(quotient, one);
1109 di_release(&quotient);
1110
1111 df_frac result = df_from_di(adjusted, one);
1112 di_release(&adjusted);
1113 di_release(&one);
1114 return result;
1115}
1116
1117// Truncate function
1119 DF_ASSERT(f && "df_trunc: operand cannot be NULL");
1120
1121 if (df_is_integer(f)) {
1122 return df_copy(f);
1123 }
1124
1125 // Truncate toward zero - same as whole_part
1126 di_int whole_int = df_whole_part(f);
1127 di_int one = di_one();
1128 df_frac result = df_from_di(whole_int, one);
1129 di_release(&whole_int);
1130 di_release(&one);
1131 return result;
1132}
1133
1134// Round function
1136 DF_ASSERT(f && "df_round: operand cannot be NULL");
1137
1138 if (df_is_integer(f)) {
1139 return df_copy(f);
1140 }
1141
1142 // Round to nearest integer, ties to even (banker's rounding)
1143 df_frac half = df_from_ints(1, 2);
1144 df_frac abs_frac_part = df_abs(df_fractional_part(f));
1145
1146 // Check if fractional part is exactly 0.5
1147 if (df_eq(abs_frac_part, half)) {
1148 // Tie case - round to even
1149 di_int whole_part = df_whole_part(f);
1150 di_int one_den = di_one();
1151 df_frac whole_frac = df_from_di(whole_part, one_den);
1152 di_release(&whole_part);
1153 di_release(&one_den);
1154
1155 // Check if whole part is even
1156 di_int whole = df_whole_part(f);
1157 int64_t whole_val;
1158 di_to_int64(whole, &whole_val);
1159
1160 if (whole_val % 2 == 0) {
1161 // Already even, return whole part
1162 df_release(&half);
1163 df_release(&abs_frac_part);
1164 di_release(&whole);
1165 return whole_frac;
1166 } else {
1167 // Odd, round to next even number
1168 di_int one = di_one();
1169 di_int adjusted_whole;
1170
1171 if (df_is_negative(f)) {
1172 adjusted_whole = di_sub(whole, one);
1173 } else {
1174 adjusted_whole = di_add(whole, one);
1175 }
1176
1177 di_int one_den = di_one();
1178 df_frac result = df_from_di(adjusted_whole, one_den);
1179 di_release(&one_den);
1180
1181 df_release(&half);
1182 df_release(&abs_frac_part);
1183 df_release(&whole_frac);
1184 di_release(&whole);
1185 di_release(&one);
1186 di_release(&adjusted_whole);
1187 return result;
1188 }
1189 } else {
1190 // Normal rounding - not a tie
1191 df_frac signed_half;
1192 if (df_is_negative(f)) {
1193 signed_half = df_negate(half);
1194 } else {
1195 signed_half = df_copy(half);
1196 }
1197
1198 df_frac adjusted = df_add(f, signed_half);
1199 df_frac result = df_trunc(adjusted);
1200
1201 df_release(&half);
1202 df_release(&abs_frac_part);
1203 df_release(&signed_half);
1204 df_release(&adjusted);
1205 return result;
1206 }
1207}
1208
1209// Sign function
1210DF_IMPL int df_sign(df_frac f) {
1211 DF_ASSERT(f && "df_sign: operand cannot be NULL");
1212
1213 if (df_is_zero(f)) return 0;
1214 if (df_is_negative(f)) return -1;
1215 return 1;
1216}
1217
1218// Min function
1220 DF_ASSERT(a && "df_min: first operand cannot be NULL");
1221 DF_ASSERT(b && "df_min: second operand cannot be NULL");
1222
1223 return df_lt(a, b) ? df_copy(a) : df_copy(b);
1224}
1225
1226// Max function
1228 DF_ASSERT(a && "df_max: first operand cannot be NULL");
1229 DF_ASSERT(b && "df_max: second operand cannot be NULL");
1230
1231 return df_gt(a, b) ? df_copy(a) : df_copy(b);
1232}
1233
1234
1235// Hash function
1236DF_IMPL uint64_t df_hash(df_frac f) {
1237 DF_ASSERT(f && "df_hash: operand cannot be NULL");
1238
1239 // Simple hash combining numerator and denominator
1240 // Use a well-known hash combining formula
1241 uint64_t h1 = 0, h2 = 0;
1242
1243 // Hash the numerator and denominator using their string representations
1244 char* num_str = di_to_string(f->numerator, 10);
1245 char* den_str = di_to_string(f->denominator, 10);
1246
1247 // Simple string hash (djb2 algorithm)
1248 for (const char* s = num_str; *s; s++) {
1249 h1 = h1 * 33 + (uint8_t)*s;
1250 }
1251 for (const char* s = den_str; *s; s++) {
1252 h2 = h2 * 33 + (uint8_t)*s;
1253 }
1254
1255 free(num_str);
1256 free(den_str);
1257
1258 // Combine hashes
1259 return h1 ^ (h2 << 1);
1260}
1261
1262// Type checking functions
1263DF_IMPL bool df_fits_int32(df_frac f) {
1264 DF_ASSERT(f && "df_fits_int32: operand cannot be NULL");
1265
1266 if (!df_is_integer(f)) return false;
1267
1268 int32_t dummy;
1269 return di_to_int32(f->numerator, &dummy);
1270}
1271
1272DF_IMPL bool df_fits_int64(df_frac f) {
1273 DF_ASSERT(f && "df_fits_int64: operand cannot be NULL");
1274
1275 if (!df_is_integer(f)) return false;
1276
1277 int64_t dummy;
1278 return di_to_int64(f->numerator, &dummy);
1279}
1280
1281DF_IMPL bool df_fits_double(df_frac f) {
1282 DF_ASSERT(f && "df_fits_double: operand cannot be NULL");
1283
1284 // Convert to double and back, see if we get the same fraction
1285 double d = df_to_double(f);
1286 if (!isfinite(d)) return false;
1287
1288 df_frac converted = df_from_double(d, 1000000); // Reasonable precision
1289 bool fits = df_eq(f, converted);
1290 df_release(&converted);
1291
1292 return fits;
1293}
1294
1295// Fraction parts
1297 DF_ASSERT(f && "df_whole_part: operand cannot be NULL");
1298
1299 // For truncation toward zero, we need to handle negative numbers differently
1300 // di_div floors toward negative infinity, but we want truncation toward zero
1301 di_int quotient = di_div(f->numerator, f->denominator);
1302
1303 // If negative and not an exact division, add 1 to get truncation behavior
1304 if (df_is_negative(f) && !df_is_integer(f)) {
1305 di_int one = di_one();
1306 di_int adjusted = di_add(quotient, one);
1307 di_release(&quotient);
1308 di_release(&one);
1309 return adjusted;
1310 }
1311
1312 return quotient;
1313}
1314
1316 DF_ASSERT(f && "df_fractional_part: operand cannot be NULL");
1317
1318 if (df_is_integer(f)) {
1319 return df_zero();
1320 }
1321
1322 // fractional_part = f - whole_part(f)
1323 // This preserves the sign: -2.333... - (-2) = -0.333...
1324 di_int whole_int = df_whole_part(f);
1325 di_int one = di_one();
1326 df_frac whole = df_from_di(whole_int, one);
1327 di_release(&whole_int);
1328 di_release(&one);
1329
1330 df_frac result = df_sub(f, whole);
1331 df_release(&whole);
1332
1333 return result;
1334}
1335
1336#endif // DF_IMPLEMENTATION
1337
1338#endif // DYNAMIC_FRACTION_H
struct df_frac_internal * df_frac
Opaque pointer to a rational number.
Reference-counted arbitrary precision integer library.
struct di_int_internal * di_int
Integer handle for arbitrary precision integers.
di_int di_gcd(di_int a, di_int b)
Greatest Common Divisor using Euclidean algorithm.
di_int di_abs(di_int a)
Get absolute value of an integer.
di_int di_add(di_int a, di_int b)
Add two integers.
di_int di_sub(di_int a, di_int b)
Subtract two integers.
di_int di_div(di_int a, di_int b)
Divide two integers using floor division.
di_int di_mul(di_int a, di_int b)
Multiply two integers.
di_int di_negate(di_int a)
Negate an integer (change sign)
df_frac df_add(df_frac a, df_frac b)
Add two fractions.
df_frac df_abs(df_frac f)
Get absolute value.
df_frac df_sub(df_frac a, df_frac b)
Subtract two fractions.
df_frac df_negate(df_frac f)
Negate a fraction.
df_frac df_div(df_frac a, df_frac b)
Divide two fractions.
df_frac df_mul(df_frac a, df_frac b)
Multiply two fractions.
df_frac df_reciprocal(df_frac f)
Get reciprocal (1/f)
bool di_is_zero(di_int big)
Test if integer is zero.
bool di_is_one(di_int big)
Test if integer equals one.
int di_compare(di_int a, di_int b)
Compare two integers.
bool di_is_negative(di_int big)
Test if integer is negative.
bool di_eq(di_int a, di_int b)
Test if two integers are equal.
bool df_gt(df_frac a, df_frac b)
Test greater than.
bool df_ne(df_frac a, df_frac b)
Test inequality.
bool df_eq(df_frac a, df_frac b)
Test equality.
bool df_le(df_frac a, df_frac b)
Test less than or equal.
bool df_ge(df_frac a, df_frac b)
Test greater than or equal.
int df_cmp(df_frac a, df_frac b)
Compare two fractions.
bool df_lt(df_frac a, df_frac b)
Test less than.
bool di_to_int32(di_int big, int32_t *result)
Convert integer to 32-bit signed integer.
double di_to_double(di_int big)
Convert integer to double precision floating point.
char * di_to_string(di_int big, int base)
Convert integer to string representation.
bool di_to_int64(di_int big, int64_t *result)
Convert integer to 64-bit signed integer.
bool df_to_int64(df_frac f, int64_t *result)
Convert to int64_t if possible.
char * df_to_string(df_frac f)
Convert to string.
di_int df_denominator(df_frac f)
Get denominator as di_int.
di_int df_numerator(df_frac f)
Get numerator as di_int.
double df_to_double(df_frac f)
Convert to double.
df_frac df_from_string(const char *str)
Parse fraction from string.
di_int di_from_string(const char *str, int base)
Create a new integer from a string representation.
di_int di_from_int64(int64_t value)
Create a new integer from a 64-bit signed integer.
di_int di_one(void)
Create a new integer with value one.
df_frac df_pow(df_frac base, int64_t exponent)
Raise fraction to integer power.
df_frac df_max(df_frac a, df_frac b)
Maximum of two fractions.
uint64_t df_hash(df_frac f)
Hash function for fractions.
bool df_fits_int32(df_frac f)
Check if fraction fits in int32_t.
df_frac df_round(df_frac f)
Round to nearest integer.
di_int df_whole_part(df_frac f)
Get integer (whole) part of fraction.
df_frac df_min(df_frac a, df_frac b)
Minimum of two fractions.
df_frac df_floor(df_frac f)
Floor function - greatest integer ≤ f.
df_frac df_trunc(df_frac f)
Truncate towards zero.
df_frac df_fractional_part(df_frac f)
Get fractional part of fraction.
int df_sign(df_frac f)
Get sign of fraction.
df_frac df_ceil(df_frac f)
Ceiling function - smallest integer ≥ f.
bool df_fits_double(df_frac f)
Check if fraction fits in double without precision loss.
bool df_fits_int64(df_frac f)
Check if fraction fits in int64_t.
df_frac df_from_di(di_int numerator, di_int denominator)
Create a fraction from a di_int numerator and denominator.
df_frac df_retain(df_frac f)
Increase reference count.
void df_release(df_frac *f)
Decrease reference count and free if zero.
df_frac df_copy(df_frac f)
Create a copy of a fraction.
df_frac df_from_ints(int64_t numerator, int64_t denominator)
Create a fraction from numerator and denominator.
df_frac df_from_double(double value, int64_t max_denominator)
Create a fraction from a double.
df_frac df_from_int(int64_t value)
Create a fraction from an integer.
bool df_is_negative(df_frac f)
Test if fraction is negative.
bool df_is_integer(df_frac f)
Test if fraction is an integer.
bool df_is_positive(df_frac f)
Test if fraction is positive.
bool df_is_one(df_frac f)
Test if fraction is one.
bool df_is_zero(df_frac f)
Test if fraction is zero.
di_int di_retain(di_int big)
Increment reference count and return the same integer.
void di_release(di_int *big)
Decrement reference count and free if zero.
df_frac df_one(void)
Create one fraction (1/1)
df_frac df_zero(void)
Create zero fraction (0/1)
df_frac df_neg_one(void)
Create negative one fraction (-1/1)
Internal structure for a rational number.