<!DOCTYPE html>
<html><html><head><meta charset="utf-8"><meta content="width=device-width, initial-scale=1.0" name="viewport"><script async="true" data-goatcounter="https://hpincket.goatcounter.com/count" src="//gc.zgo.at/count.js"></script><link href="/static/css/main--1543361993.css" rel="stylesheet"><link href="https://fosstodon.org/@hpincket" rel="me"><link href="/feeds/all.atom.xml" rel="alternate" title="hpincket (all)" type="application/atom+xml"></head><body><header><nav><div class="wordmark"><a href="/"><img alt="hpincket" src="/wordmark.png" width="120"></a></div><div class="menu-item"><a href="/pages/about-me.html">About</a></div><div class="menu-item"><a href="/pages/reference.html">Reference</a></div><div class="menu-item"><a href="/category/technical.html">Technical</a></div><div class="menu-item"><a href="/category/scrapbook.html">Scrapbook</a></div><div class="menu-item"><a href="/category/misc.html">Misc</a></div></nav></header><main><h1 class="entry-title">Representing rationals with Integer Constraints</h1><time class="published" datetime="2024-09-29T00:00:00-04:00[America/New_York]">Sun 29 September 2024</time><p>This is a quick and dirty post attempting rational (fraction) math with Prolog's integer constraint library. CLPFD works well for integer math, but it is not built to handle decimal values. This limits where you can use CLPFD, if a domain has even a single non-integer value, you'll have to work around it.</p><p>As a challenge, I attempted to represent fractions in CLPFD. It's not too hard, just store a numerator and denominator. Pass them around together.</p><p>I started off with a failed attempt to write a predicate to reduce the fraction: I reproduce it here out of humbleness.</p><pre><code class="prolog">:- use_module(library(clpfd)).

% The fraction C/D is the reduction of A / B
% This does not work.
reduce(A, B, C, D) :-
  % If there is some X that is in both the numerator and denominator...
  [A1, B1] ins 1..100,
  X in 2..10,
  XExists #&lt;==&gt; (
    A #= A1 * X #/\ B #= B1 * X),
  write(XExists),
  % XExists #==&gt; (
  % reduce(A1, B1, C, D),
  % ),
  (#\ XExists) #==&gt; (
    A #= C #/\ B #= D
  ).
</code></pre><p>After 30 minutes I started searching the web. I found this helpful <a href="ttps://stackoverflow.com/questions/66016948/inverting-a-function-via-clpfd-in-pure-prolog">SO post</a> which implemented Euclid's algorithm in CLPFD.</p><pre><code>% From: https://stackoverflow.com/questions/66016948/inverting-a-function-via-clpfd-in-pure-prolog
euclid(A,A,A).
euclid(A,B,R) :- A #&lt; B, C #= B-A, euclid(A,C,R).
euclid(A,B,R) :- A #&gt; B, C #= A-B, euclid(C,B,R).
</code></pre><p>Bested by an ancient Greek, I returned to my task</p><p>The rest was simple:</p><pre><code class="prolog">
reduce2(A, B, C, D) :-
  euclid(A, B, R),
  C #= A // R,
  D #= B // R.

multiply_rational(A, B, C, D, E, F) :-
  E0 #= A * C,
  F0 #= B * D,
  reduce2(E0, F0, E, F).

add_rational(A, B, C, D, E, F) :-
  A1 #= A * D,
  C1 #= C * B,
  F0 #= B * D,
  E0 #= A1 + C1,
  reduce2(E0, F0, E, F).

invert(A, B, B, A).

divide_rational(A, B, C, D, E, F) :-
  invert(C, D, C0, D0),
  multiply_rational(A, B, C0, D0, E, F).

greater_than_rational(A, B, C, D, T) :-
  % Generate same denominator
  A1 #= A * D,
  C1 #= C * B,
  T #&lt;==&gt; A1 #&gt; C1.

equal_rational(A, B, C, D, T) :-
  reduce2(A, B, E, F),
  reduce2(C, D, E2, F2),
  T #&lt;==&gt; E #= E2 #/\ F #= F2.
</code></pre><p>This is pretty scrappy, but it's a starting point for expanding CLPFD to more domains.</p></main></body></html></html>