using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Numerics;

class Program
{
    const int MOD = 1000000007;
    static void Main(string[] args)
    {
        new Program().Solve();
    }

    void Solve()
    {
        Scanner cin = new Scanner();
        long a = cin.NextLong();
        long b = cin.NextLong();
        long c = cin.NextLong();
        long d = cin.NextLong();

        long numOfCandidates = b - a + 1;

        long divisablesByCCovered = b / c;
        long divisablesByCNonCovered = CountUnCovered(a, c);
        long numOfDivisablesByC = divisablesByCCovered - divisablesByCNonCovered;

        long divisablesByDCovered = b / d;
        long divisablesByDNonCovered = CountUnCovered(a, d);
        long numOfDivisablesByD = divisablesByDCovered - divisablesByDNonCovered;

        long lcm = LCM(c, d);
        long duplicatedCovered = b / lcm;
        long duplicatedNonConvered = CountUnCovered(a, lcm);
        long numOfDuplicated = duplicatedCovered - duplicatedNonConvered;

        long ans = numOfCandidates - (numOfDivisablesByC + numOfDivisablesByD - numOfDuplicated);

        var sw = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false };
        Console.SetOut(sw);

        Console.WriteLine(ans);

        Console.Out.Flush();       
    }

    long CountUnCovered(long lowerBound, long divisor)
    {
        if (lowerBound % divisor == 0)
        {
            return lowerBound / divisor - 1;
        }
        return lowerBound / divisor;
    }

    /// <summary>
    ///  O(log n)
    /// </summary>
    /// <param name="a"></param>
    /// <param name="b"></param>
    /// <returns></returns>
    static long LCM(long a, long b)
    {
        long gcd = GCD(a, b);
        return a * b / gcd;
    }

    /// <summary>
    ///  calculate gcd in O(log n).
    /// </summary>
    /// <param name="a">a must be equal or larger than b.</param>
    /// <param name="b"></param>
    /// <returns></returns>
    static long GCD(long a, long b)
    {
        long c = 1;
        while (c != 0)
        {
            c = a % b;
            a = b;
            b = c;
        }
        return a;
    }
}


class Scanner
{
    string[] s;
    int i;

    char[] cs = new char[] { ' ' };

    public Scanner()
    {
        s = new string[0];
        i = 0;
    }

    public string Next()
    {
        if (i < s.Length) return s[i++];
        string st = Console.ReadLine();
        while (st == "") st = Console.ReadLine();
        s = st.Split(cs, StringSplitOptions.RemoveEmptyEntries);
        if (s.Length == 0) return Next();
        i = 0;
        return s[i++];
    }

    public int NextInt()
    {
        return int.Parse(Next());
    }

    public char[] ArrayChar()
    {
        return Next().ToCharArray();
    }
    public int[] ArrayInt(int N, int add = 0)
    {
        int[] Array = new int[N];
        for (int i = 0; i < N; i++)
        {
            Array[i] = NextInt() + add;
        }
        return Array;
    }

    public long NextLong()
    {
        return long.Parse(Next());
    }

    public long[] ArrayLong(int N, long add = 0)
    {
        long[] Array = new long[N];
        for (int i = 0; i < N; i++)
        {
            Array[i] = NextLong() + add;
        }
        return Array;
    }

    public double NextDouble()
    {
        return double.Parse(Next());
    }


    public double[] ArrayDouble(int N, double add = 0)
    {
        double[] Array = new double[N];
        for (int i = 0; i < N; i++)
        {
            Array[i] = NextDouble() + add;
        }
        return Array;
    }
}

static class Func
{
    public static Comparison<T> ToComparison<T>(this IComparer<T> comp) => comp == null ? DefaultComparison<T>() : (x, y) => comp.Compare(x, y);
    public static Comparison<T> DefaultComparison<T>() => (x, y) => Comparer<T>.Default.Compare(x, y);
}
class PriorityQueue<T> : IEnumerable<T>, ICollection, IEnumerable, ICloneable
{
    Comparison<T> comp;
    List<T> list;
    public int Count { get; private set; } = 0;
    public bool IsEmpty => Count == 0;
    public PriorityQueue(IEnumerable<T> source) : this((Comparison<T>)null, 0, source) { }
    public PriorityQueue(int capacity = 4, IEnumerable<T> source = null) : this((Comparison<T>)null, capacity, source) { }
    public PriorityQueue(IComparer<T> comp, IEnumerable<T> source) : this(comp.ToComparison(), source) { }
    public PriorityQueue(IComparer<T> comp, int capacity = 4, IEnumerable<T> source = null) : this(comp.ToComparison(), source) { list.Capacity = capacity; }
    public PriorityQueue(Comparison<T> comp, IEnumerable<T> source) : this(comp, 0, source) { }
    public PriorityQueue(Comparison<T> comp, int capacity = 4, IEnumerable<T> source = null) { this.comp = comp ?? Func.DefaultComparison<T>(); list = new List<T>(capacity); if (source != null) foreach (var x in source) Enqueue(x); }
    /// <summary>
    /// add an item
    /// this is an O(log n) operation
    /// </summary>
    /// <param name="x">item</param>
    public void Enqueue(T x)
    {
        var pos = Count++;
        list.Add(x);
        while (pos > 0)
        {
            var p = (pos - 1) / 2;
            if (comp(list[p], x) <= 0) break;
            list[pos] = list[p];
            pos = p;
        }
        list[pos] = x;
    }
    /// <summary>
    /// return the minimum element and remove it
    /// this is an O(log n) operation
    /// </summary>
    /// <returns>the minimum</returns>
    public T Dequeue()
    {
        var value = list[0];
        var x = list[--Count];
        list.RemoveAt(Count);
        if (Count == 0) return value;
        var pos = 0;
        while (pos * 2 + 1 < Count)
        {
            var a = 2 * pos + 1;
            var b = 2 * pos + 2;
            if (b < Count && comp(list[b], list[a]) < 0) a = b;
            if (comp(list[a], x) >= 0) break;
            list[pos] = list[a];
            pos = a;
        }
        list[pos] = x;
        return value;
    }
    /// <summary>
    /// look at the minimum element
    /// this is an O(1) operation
    /// </summary>
    /// <returns>the minimum</returns>
    public T Peek() => list[0];
    public IEnumerator<T> GetEnumerator() { var x = (PriorityQueue<T>)Clone(); while (x.Count > 0) yield return x.Dequeue(); }
    void CopyTo(Array array, int index) { foreach (var x in this) array.SetValue(x, index++); }
    public object Clone() { var x = new PriorityQueue<T>(comp, Count); x.list.AddRange(list); return x; }
    public void Clear() { list = new List<T>(); Count = 0; }
    public void TrimExcess() => list.TrimExcess();
    /// <summary>
    /// check whether item is in this queue
    /// this is an O(n) operation
    /// </summary>
    public bool Contains(T item) => list.Contains(item);
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    void ICollection.CopyTo(Array array, int index) => CopyTo(array, index);
    bool ICollection.IsSynchronized => false;
    object ICollection.SyncRoot => this;
}

Measured memory_bytes: 13140