import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.TreeSet;


/**
 * ICPC - CTU Open Contest 2019
 * Sample Solution: Be Geeks (Max*GCD for all intervals)
 * 
 * @author Martin Kacer
 */
public class MaxTimesGcd {
	StringTokenizer st = new StringTokenizer("");
	BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
	boolean hasNextToken() {
		try {
			while (!st.hasMoreTokens()) {
				String line = input.readLine();
				if (line == null) return false;
				st = new StringTokenizer(line);
			}
		} catch (IOException ex) { throw new RuntimeException(ex); }
		return true;
	}
	String nextToken() {
		return (!hasNextToken()) ? null : st.nextToken();
	}
	int nextInt() {
		return Integer.parseInt(nextToken());
	}
	public static void main(String[] args) {
		findPrimes();
		new MaxTimesGcd().run();
	}

	static final int MAXSIZE = 200_000, MAXNUM = 1_000_000_000, MOD = 1_000_000_007,
			MAXPRIM = 5000, MAXFACT = 30;
	static int primcnt, primes[], NONE[] = new int[0];
	
	int[] nums, tmp = new int[MAXFACT];
	int[][] fact, fprev, fnext;
	
	static void findPrimes() {
		primcnt = 0;
		primes = new int[MAXPRIM];
		ploop: for (int p = 2; p*p <= MAXNUM; ++p) {
			for (int i = 0; i < primcnt; ++i) if (p % primes[i] == 0) continue ploop;
			primes[primcnt++] = p;
		}
	}
	
	void run() {
		findPrimes();
		int n = nextInt();
		nums = new int[n];
		fact = new int[n][]; fprev = new int[n][]; fnext = new int[n][];
		for (int i = 0; i < n; ++i)
			fact[i] = factorize(nums[i] = nextInt());
		for (int i = 0; i < n; ++i) fprev[i] = next(fact[i], i>0 ? fact[i-1] : NONE, i>0 ? fprev[i-1] : NONE);
		for (int i = n; --i >= 0; )
			fnext[i] = next(fact[i], i+1<n ? fact[i+1] : NONE, i+1<n ? fnext[i+1] : NONE);
		
		Integer[] indexes = new Integer[nums.length];
		for (int i = 0; i < nums.length; ++i) indexes[i] = i;
		Arrays.sort(indexes, (a, b) -> nums[b] - nums[a]);
		TreeSet<Integer> processed = new TreeSet<>();
		processed.add(-1); processed.add(n);
		long sum = 0;
		for (int i : indexes) {
			sum = (sum + solve(i, processed.lower(i) + 1, processed.higher(i))) % MOD;
			processed.add(i);
		}
		System.out.println(sum);
	}
	
	int[] factorize(int x) {
		int ti = 0;
		for (int p = 0; p < primcnt && x > 1; ++p)
			while (x % primes[p] == 0) x /= (tmp[ti++] = primes[p]);
		if (x > 1) tmp[ti++] = x;
		return Arrays.copyOf(tmp, ti);
	}
	
	long solve(int max, int start, int end) {
		TreeMap<Integer,Integer> left = oneSide(fact[max], fprev[max], max-start),
				right = oneSide(fact[max], fnext[max], end-max-1);
		long sum = 0;
		int pl = -1;
		for (Map.Entry<Integer,Integer> mle : left.entrySet()) {
			int pr = -1;
			for (Map.Entry<Integer,Integer> mre : right.entrySet()) {
				long val = nums[max] * (long) gcd(mle.getValue(), mre.getValue());
				sum = (sum + (val * (mle.getKey()-pl) % MOD * (mre.getKey()-pr))) % MOD;
				pr = mre.getKey();
			}
			pl = mle.getKey();
		}
		return sum;
			
	}
	TreeMap<Integer,Integer> oneSide(int[] f, int[] prv, int limit) {
		TreeMap<Integer,Integer> res = new TreeMap<>();
		res.put(limit, 1);
		for (int i = 0; i < f.length; ++i) {
			int pos = (prv[i] < limit) ? prv[i] : limit;
			res.put(pos, f[i] * res.getOrDefault(pos, 1));
		}
		int mul = 1;
		for (Map.Entry<Integer,Integer> me : res.descendingMap().entrySet())
			me.setValue(mul *= me.getValue());
		return res;
	}
	
	int[] next(int[] factn, int[] factp, int[] prev) {
		for (int n = 0, p = 0; n < factn.length; ++n) {
			while (p < factp.length && factp[p] < factn[n]) ++p;
			tmp[n] = (p < factp.length && factp[p] == factn[n]) ? prev[p++] + 1 : 0;
		}
		return Arrays.copyOf(tmp, factn.length);
	}

	static int gcd(int a, int b) {
		return (b == 0) ? a : gcd(b, a % b);
	}
}
