CREATE OR REPLACE FUNCTION number_to_words(num INTEGER) RETURNS TEXT AS $$ DECLARE units TEXT[] := ARRAY['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']; teens TEXT[] := ARRAY['', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen']; tens TEXT[] := ARRAY['', 'ten', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety']; thousands TEXT[] := ARRAY['', 'thousand', 'million', 'billion']; num_text TEXT := ''; temp_num INTEGER; segment INTEGER; segment_text TEXT; index INTEGER := 0; BEGIN IF num = 0 THEN RETURN 'zero'; END IF; IF num < 0 THEN RETURN 'negative ' || number_to_words(ABS(num)); END IF; WHILE num > 0 LOOP segment := num % 1000; num := num / 1000; IF segment > 0 THEN segment_text := ''; -- Handle hundreds place IF segment >= 100 THEN segment_text := units[segment / 100] || ' hundred '; segment := segment % 100; END IF; -- Handle tens and units place IF segment >= 20 THEN segment_text := segment_text || tens[segment / 10]; segment := segment % 10; IF segment > 0 THEN segment_text := segment_text || '-' || units[segment]; END IF; ELSIF segment >= 11 AND segment <= 19 THEN segment_text := segment_text || teens[segment - 10]; ELSE segment_text := segment_text || units[segment]; END IF; -- Add the segment text with its thousand place num_text := segment_text || ' ' || thousands[index] || ' ' || num_text; END IF; index := index + 1; END LOOP; RETURN trim(both ' ' FROM num_text); END; $$ LANGUAGE plpgsql;